Live data from Hacker News

Structured Errors in Go (2022)

southcla.ws

51–60 of 75 posts

Re: Structured Errors in Go (2022)

#51
post #10

The implementation of WithMeta() is flawed. Not only is it not concurrency-safe, every nested call will be modifying the parent map. The way to do this in a safe and performant manner is to structure the metadata as a tree, with a parent pointing to the previous metadata. You'd probably want to do some pooling and other optimizations to avoid allocating a map every time. Then all the maps can be immutable and therefo…

Just a little more work, and you will reinvent exceptions with stack traces! Proper error handling in Go is now tantalisingly close!

errors aren’t exception

Re: Structured Errors in Go (2022)

#52
post #44

Earlier quoted context omitted.

err.(type) fails when errors are wrapped, which is common and needs to be accommodated. To do what you're trying to do, you need to use errors.As.

Sure. It was not intended to be a complete example.

It's not that the example is incomplete, it's that it's incorrect! :)

Re: Structured Errors in Go (2022)

#53
post #16
post #4

Alright, so this looks pretty comprehensive for error handling. But I gotta ask – for smaller to mid-size projects, is there a point where this level of structure becomes more work than it's worth?

IMO error handling is the sort of thing you really want to get right early on, even in toy projects. It’s very hard to retrofit, and the actual payoff is low until you need it - at which point you definitely don’t want to do the work. As antithetical as it might be, I tend to just stuff sentry in (no affiliation just a happy user) when I’m setting up the scaffolding, and insert rich context at the edges (in the route…

why do you think it's hard to retrofit? it's just an SDK to setup up, right (point at some DSN)?

Re: Structured Errors in Go (2022)

#54
post #21
post #3

One thing that seemingly is missing is the ability to tag a specific error with an error code. You typically want to know that all of a sudden the ”failed to get user” error is being returned a lot. Since the message is a dynamic string you can’t just group by the string so unless you build it as part of your abstraction it becomes very hard to do. Edit: looking more carefully at the lib I assume that ”tag” is the co…

in standard go you'd have errors implement: func (myError) Is(err error) bool and it can match different sentinel errors. Or you can make your own wrapper to have the error chain match.

In standard go you would not implement Is but would use the errors.Is call. How would this work if it is a sentinel error as you would use with errors.Is??

Re: Structured Errors in Go (2022)

#55
post #54
post #21

Earlier quoted context omitted.

in standard go you'd have errors implement: func (myError) Is(err error) bool and it can match different sentinel errors. Or you can make your own wrapper to have the error chain match.

In standard go you would not implement Is but would use the errors.Is call. How would this work if it is a sentinel error as you would use with errors.Is??

https://pkg.go.dev/errors#Is

> An error is considered to match a target if it is equal to that target or if it implements a method Is(error) bool such that Is(target) returns true.

by default errors.Is matches the exact error variable, but you can use it to match other errors as well.

Re: Structured Errors in Go (2022)

#56
post #55
post #54

Earlier quoted context omitted.

In standard go you would not implement Is but would use the errors.Is call. How would this work if it is a sentinel error as you would use with errors.Is??

https://pkg.go.dev/errors#Is > An error is considered to match a target if it is equal to that target or if it implements a method Is(error) bool such that Is(target) returns true. by default errors.Is matches the exact error variable, but you can use it to match other errors as well.

I stand corrected. Still you cannot implement an Is on a sentinel error and is only applicable to concrete error types. And if I'm using concrete types I'm going to us errors.As

Re: Structured Errors in Go (2022)

#57
post #52

Earlier quoted context omitted.

Sure. It was not intended to be a complete example.

It's not that the example is incomplete, it's that it's incorrect! :)

No, it's just incomplete. The same code just needs to unwrap:

    for err != nil {
      switch e := err.(type) {
        case UserNotFound:
          writeJSONResponse(w, 404, "User not found")
          return
        case interface { Timeout() bool }:
          if e.Timeout() {
            writeJSONResponse(w, 503, "Timeout")
            return
          }
      }
      err = errors.Unwrap(err)
    }

Re: Structured Errors in Go (2022)

#58

These are good general tips applicable to other languages too. I strongly dislike when code returns errors as arbitrary strings rather than classes, as it makes errors extremely difficult to handle; one would presumably want to handle a http 502 diffrernetly to a 404, but if a programmer returns that in a string, I have to do some wonky regex instead of checking the type of error class (or pulling a property from an…

I couldn't agree more. I was surprised to see the default go error handling when I switched to the language a few years ago. Any meaningful REST API implementation, as you say, needs to know what to return to the user. Perhaps there is an error for the user, and then an error for the logs. With the default go mechanism, it's too easy to return system information to the user, potentially revealing database schemas.

Re: Structured Errors in Go (2022)

#59
post #16

Earlier quoted context omitted.

IMO error handling is the sort of thing you really want to get right early on, even in toy projects. It’s very hard to retrofit, and the actual payoff is low until you need it - at which point you definitely don’t want to do the work. As antithetical as it might be, I tend to just stuff sentry in (no affiliation just a happy user) when I’m setting up the scaffolding, and insert rich context at the edges (in the route…

why do you think it's hard to retrofit? it's just an SDK to setup up, right (point at some DSN)?

The sdk setup is a breeze, but retrofitting all of the bespoke one off error logs and tracing to a common subset to send via the SDK is not.

Re: Structured Errors in Go (2022)

#60
post #8

Not a Go engineer but Go-curious - shouldn’t this use slog[0] for structured logging rather than a third party?

Depends on if you're making a public library. Using slog is "polite" but I don't really see it as the endgame of logging libraries. It has quite a few rough edges for CLI apps, like no control on attr order. But it is zomgfast, and speed is important, right?

The biggest advantage is being third-party-dependency-free. I tend to reject libraries which have no updated to use the stdlib approach at this stage as “likely unmaintained” - similar for those that pull in things like pkg/errors transitively. Apps can do what they like, though.
Post reply on HN