No mention of `errors.Is` or error wrapping? IMO that's where the pain starts.<p>I definitely agree that in many programs, when an error happens, you're done, you just blast up the stack and then the error message goes into a sentry report or gets logged etc.<p>But it's also still quite common to want to treat some error conditions as soft errors, or otherwise handle them differently. Now you're doing errors.Is and engaging with the poorly structured world of error wrapping trees.<p>Here's one I hit recently. One of my pet peeves is error messages like "unsupported thing". Ok, what thing was unsupported? Very often people just omit this stuff because it requires you to now cat strings, which is sometimes hard or cumbersome depending on the language. So, I changed my code to have the error be "unsupported thing: <thing>".<p>However, up the call stack, I want to handle ErrUnsupportedThing too. Because maybe there's some reasonable thing I can do to help the user make the thing supported. So I'm calling `errors.Is`. But wait, now I need an `ErrUnsupportedThing` variable to refer to. But each call to fmt.Error creates a new error that isn't `errors.Is`-equivalent to each other.<p>I ended up reconciling this by making `ErrUnsupportedThing` just be "unsupported thing" and then I wrap it by doing fmt.Errorf("%w: %v", ErrUnsupportedThing, thingType).<p>But now what if I want to say "ErrUnsupportedThing: <A sub-error that occurred>"? Well, hope you're using a recent version of go, because "%w: %w" only works in go 1.20 and later.<p>Also the fact that all errors are just "error" instead of being typed errors tends to make people a lot lazier about their error handling in general. In java, if I get a ThingNotSupportedException, I expect to be able to find a getUnsupportedThingType() so that I can explicitly pull it off the string without needing to parse. That's easy in Java, it's a subclass of exception. In go, now you're writing a "subclass" struct to implement error's interface, tacking on extra struct fields and stuff, and since nobody ever actually types their return values more explicitly than `error`, you're now doing casting/duck-typing to pull that information out. Etc.<p>I guess let me put it this way. Go errors are good for 80% of errors and fine for 95%. That's great. But the easy code is already easy! What irks me is when it breaks down when I get to the code that's actually a bit tricky to handle errors for. The type system, the error system, and everything is just like "wow good luck you're on your own". Whereas python/java/etc have my back here.