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

Support wrapped errors #504

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

rkennedy
Copy link

@rkennedy rkennedy commented May 1, 2024

Wrapping errors instead of merely embedding error messages allows calling code to use errors.Is and errors.As to more precisely check the reasons for various errors instead of having to rely only on substring searches.

Fixes #503

Since error-wrapping was only introduced in Go 1.13, accepting this PR would involve dropping support for 1.11 and 1.12 (and even earlier, if the readme's claim of supporting all the way back to 1.7 is still accurate). I realize that may jeopardize this PR because it's definitely not a change to make lightly.

@TotallyGamerJet
Copy link

Why not just implement a wrapped error type specifically for mage? That way version compatibility can be maintained

Wrapping errors instead of merely embedding error messages allows
calling code to use `errors.Is` and `errors.As` to more precisely check
the reasons for various errors instead of having to rely only on
substring searches.

We implement our own wrapper error to retain backward compatibility
prior to Go 1.13, where there is no support for the "%w" format string
in `fmt.Errorf`.
@rkennedy
Copy link
Author

rkennedy commented May 2, 2024

Why not just implement a wrapped error type specifically for mage? That way version compatibility can be maintained

Well, because my initial reaction was that "just" was doing quite a bit of work in that question! But it turned out not to be so bad after all. I didn't have to do anything crazy like re-implement errors.Is, just write a wrapper that implements Error() and Unwrap(), and then add a bunch of calls to use the new error type. The call sites look cumbersome within this library, but consumers of the library should never notice a difference. Using build tags, we can ensure that the new functionality is tested on versions that support it.

Thanks for the nudge toward a better solution.

@rkennedy rkennedy changed the title Use %w to wrap upstream errors Support wrapped errors May 2, 2024
@TotallyGamerJet
Copy link

Do we want to actually export the WrapError function for anyone to use? If not, I'd move it into the internal/ package.

Also, why not make the function signature like this? It improves the calling site.

 WrapErrorF(err, "error running \"%s %s\": %v\n%s", cmd, strings.Join(args, " "), err, errMsg)

func WrapErrorF(underlying error, format string, args ...interface{}) error {
	return &wrappedError{
		underlyingError: underlying,
		stringError:     fmt.Errorf(format, args...),
	}
}

@TotallyGamerJet
Copy link

Thanks for the nudge toward a better solution.

Ofc glad I could help!

There's no particular reason to make error-wrapping be part of Mage's
public interface, so now it's kept in the internal package. One
consolation was needed to avoid circular imports, which is that when
`mg.F` panics, it does not _wrap_ the JSON-marshalling error.
Instead of having the caller create an error with fmt.Errorf and then
pass it to WrapError, we now have WrapErrorf that wraps the error and
formats a string error at once.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bug: Errors aren't created with %w, breaking errors.Is
2 participants