Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Fix TerminalError(nil).Error() panic #2438

Merged
merged 1 commit into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkg/reconcile/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,15 @@ type terminalError struct {
err error
}

// This function will return nil if te.err is nil.
func (te *terminalError) Unwrap() error {
return te.err
}

func (te *terminalError) Error() string {
if te.err == nil {
return "nil terminal error"
}
Comment on lines +121 to +123
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a common pattern? We're at this point creating an error with a nil cause/inner error which might throw off the reconciliation loop.

Copy link
Contributor Author

@sheidkamp sheidkamp Aug 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only see it used here:

if errors.Is(err, reconcile.TerminalError(nil)) {

which doesn't totally make sense because the previous line checks for err==nil (<- strikethrough because checking for err==nil does not mean that the unwrapped error is not nil)

An alternate solution would be to modify the controller to create that error with a non-nil value (empty string?) and maybe add protection in the TerminalError() function to not allow nil values.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@alvaroaleman alvaroaleman Aug 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nil terminal error there is only used for the errors.Is comparison, in that context, the Error() method will never be called.

It isn't really expected to construct a terminal error with a nil inner error when returning it, as that means logging will not be able to tell you what the issue was. When did you run into this panicing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We run into this error when using the Eris error handling library. Example here:
https://go.dev/play/p/4N0EmuNcPHJ

errors.Is calls the 1st argument's Is method here: https://cs.opensource.google/go/go/+/master:src/errors/wrap.go;l=54?q=wrap.go&ss=go%2Fgo
And then Eris' Is function does call target.Error()
https://github.com/rotisserie/eris/blob/d0c56b6433ca96eedf3c9482ec51456aa80dd772/eris.go#L212
leading to the panic.

return "terminal error: " + te.err.Error()
}

Expand Down
5 changes: 5 additions & 0 deletions pkg/reconcile/reconcile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,10 @@ var _ = Describe("reconcile", func() {

Expect(apierrors.IsGone(terminalError)).To(BeTrue())
})

It("should handle nil terminal errors properly", func() {
err := reconcile.TerminalError(nil)
Expect(err.Error()).To(Equal("nil terminal error"))
})
})
})