The purpose of error wrapping is to give contextual information about the error. It is supposed to help you understand and locate the error. For instance, *"invalid filename"* is not particularly useful when compared to *"fail to load customer data: invalid filename"*. Error wrapping is a stack trace in a human-friendly language. It is not a very efficient way to trace errors, but it explains to you like a good friend about what went wrong.
The problem with error wrapping is that it is difficult to write good error messages. I have seen wrapped error messages that are very wordy and yet don't add much information beyond the original error messages. A stack trace is a superior alternative to error wrapping. It allows you to accurately trace the error and doesn't require much effort on your part beyond writing the original error messages. On Thursday, December 30, 2021 at 4:49:07 PM UTC+7 Brian Candler wrote: > In your code, the cache layer always returns a non-nil error, even when > the value was found in the cache *and* the value was error-free. I don't > think that's realistic. You're changing "error" from an indication of a > problem, into auxiliary metadata about the value being returned (e.g. "this > value came from the cache"). It means the consumer can no longer use "err > != nil" to detect a problem, which is the most basic contract for error. > > This can be fixed: > https://go.dev/play/p/b_xLb06goXH > > But then you lose the provenance information for for error-free data (i.e. > did it come from the cache or not?). And I still don't like the idea that > a *real* error that happened can sometimes be wrapped in a cache error and > sometimes not. I think that's because the cache error isn't really a > caching error (the cache retrieved the value successfully!), but an > auxiliary indication that this error value was retrieved via the cache. > The error itself is really the same. > > The point of this thread was whether wrapping is necessary, or what the > alternative approaches are. I would suggest here that you can avoid > wrapping by following the way fs.PathError works, which is a typed error > rather than a singleton: > https://go.dev/play/p/a3cc4Watwie > > That still doesn't give you the metadata "this [valid/error] value came > from cache". I believe that information belongs in a separate channel from > error. For example, you could return a UserData struct which contains the > data you asked for, with a separate field for its provenance - or else > return a completely separate provenance value (not part of the error). > > On Thursday, 30 December 2021 at 00:58:04 UTC Mine GO BOOM wrote: > >> In practice, wrapping errors is handy for interfaces that have predefined >> generic error types that will end up being logged. In code, you'd check the >> error against that constant error with errors.Is(err, gateway.InvalidUser) >> and handle it the same way, but when you log that error the extra messaging >> will assist in debugging the problem. >> >> Imagine you have a gateway that provides details about users, and you >> have different implementations which read from redis and one that reads >> from the database. You code would call CheckUser() and the implementation >> would first check redis, see it is missing, then check your database, and >> still see it is missing. The redis layer would pick up on that InvalidUser >> error, cache it, and return the unmodified database error. The calling code >> would do a simple errors.Is(err, gateway.InvalidUser), log the error >> message, and continue processing how it needs to. The next time that user >> is check again, it hits redis and see that it is an invalid user. Redis >> returns a wrapped InvalidUser error stating that it knows it is an invalid >> user at the cache layer, maybe including the time it was stored. The >> calling code checks errors.Is(err, gateway.InvalidUser), logs, and >> continues. >> >> But at that same time, the user was created in the database but the cache >> wasn't invalidated. That user fails to log into the system, and reports a >> bug. The developer investigates the log, sees that first database >> InvalidUser log message, followed by a bunch of redis InvalidUser log >> lines, intermixed with a log message saying the user was created. They >> investigate the user creation code and notice it doesn't invalid the cache. >> They update the code, flush the redis cache for invalid users, and resolved >> a tricky caching issue that could have taken them a long time to debug >> since their recreation steps may not have been to try to first login as an >> invalid user before creating the user. >> >> As a toy example, check out: https://go.dev/play/p/7RWsj0HW1xR >> >> >> >> On Sunday, December 26, 2021 at 9:57:42 AM UTC-8 Brian Candler wrote: >> >>> ISTM the alternative is return a semantically-meaningful error at the >>> current level of execution. >>> >>> e.g. if you're trying to open a config file and it fails, you return >>> "Unable to open config file"; or better, include the text version of the >>> underlying error: e.g. "Unable to open config file: 'foo': Permission >>> denied". >>> >>> This is different from just returning the underlying error, and also >>> different from returning a wrapped error where the caller can explicitly >>> extract and match the type/value of the underlying error. >>> >>> Wrapping invites people to couple to the underlying error types. Having >>> said that: if you don't wrap, and someone really wants to branch based on >>> the underlying condition, they will match on the text content of the >>> message (which is worse IMO). >>> >>> On Sunday, 26 December 2021 at 17:44:35 UTC fli...@flimzy.com wrote: >>> >>>> Yes, of course I'm quite familiar with Go 1.13's error capabilities. >>>> github.com/pkg/errors is still useful when one wants to attach stack >>>> traces to errors. >>>> >>>> But this leaves my question open: What are alternatives to wrapping >>>> errors? (Other than passing around errors with no semantic meaning, as was >>>> the case before wrapping was widespread) >>>> >>>> On Thursday, December 23, 2021 at 8:51:20 PM UTC+1 Kevin Chowski wrote: >>>> >>>>> Have you seen https://go.dev/blog/go1.13-errors? >>>>> >>>>> If one wants to use error unwrapping, these days generally it is >>>>> suggested to use the functionality from the standard package "errors" (or >>>>> fmt.Errorf) rather than some third-party package. That way everyone does >>>>> it >>>>> the same way, making third-party packages more composable by default. >>>>> >>>>> I personally wrap errors with fmt.Errorf and the %w verb when I want >>>>> to add additional context to some error I received, and the root cause of >>>>> why my function failed is *precisely* the same as the 'error' value I >>>>> received. This is less often than I used to think, and it is definitely a >>>>> maintenance risk when this error comes from another package: if that >>>>> package changes what error type it returns, that may break some other >>>>> part >>>>> of my code. (that risk is perhaps one reason why folks choose not to wrap >>>>> errors by default, though note that errors from Go's stdlib are less >>>>> likely >>>>> to change over time than some random third-party library.) >>>>> >>>>> In general, I prefer to wrap errors that come from a package that I >>>>> own (again assuming the root cause is exactly the same AND I want to add >>>>> some annotation), and prefer not to wrap errors that come from a package >>>>> owned by someone else. But neither are hard rules for code I write or >>>>> review. >>>>> >>>>> One last thought is: if you never unwrap an error, there are fewer >>>>> good reasons to ever wrap them. If you want to unwrap them, then it >>>>> obviously starts to make sense to wrap some :) >>>>> >>>>> On Thursday, December 23, 2021 at 5:59:22 AM UTC-7 fli...@flimzy.com >>>>> wrote: >>>>> >>>>>> I was recently catching up on the latest state of the >>>>>> github.com/pkg/errors package ( >>>>>> https://github.com/pkg/errors/issues/245), when I noticed that Dave >>>>>> Cheney said: >>>>>> >>>>>> > I no longer use this package, in fact I no longer wrap errors. >>>>>> >>>>>> I'm curious what approach Dave uses now, but unless/until he writes >>>>>> on the topic, I'm also curious what other approaches people use for >>>>>> clean >>>>>> and cohesive error handling and reporting. >>>>>> >>>>>> If you don't wrap errors, how do you ensure meaningful error handling >>>>>> and reporting in your application? >>>>>> >>>>>> Thanks, >>>>>> Jonathan >>>>> >>>>> -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/7df5d9ec-9466-4b6f-80f3-d8b310463704n%40googlegroups.com.