I agree that there is room for improvement, but I don’t mind Go’s errors that much. Using a linter to make sure errors are checked doesn’t seem like a major problem (you have to run a linter anyway, so what’s the harm?); most Go developers reflexively check errors for everything besides fmt.Println anyway. It would be better to put this in the compiler I suppose, but not a major deal.
Also worth noting that Rust doesn’t require you to use errors either; unused errors are a warning in the compiler unless the return type is a result AND you’re trying to access the valid data. This is a better than Go, but not by much in practice.
The error interface doesn’t bother me too much either. Just use errors.Is/As to determine the type of you’re going to do something special with it. It’s way better than having to create unique error/result types for every function.
Who should add context is definitely a problem, but I’ve settled on “the callee”, but in cases where you’re calling something that doesn’t add context you will need to add it in the immediate caller.
Adding context in Go is significantly easier than in other languages (yes, you can use anyhow in Rust, but it’s not considered good practice to put this in library code), and good context largely obviates the need for stack traces anyway. Context is nicer because it can tell you, for example, which loop iteration you were in when things blew up or what the salient parameter values were—stuff you don’t get from a stack trace. Of course, you have to do a bit of work for this benefit, but fmt.Errorf makes this super easy.
Logging also irks me. You can pass a logger like any other data, but mostly people just use global loggers. I haven’t had the multiple loggers problem, but that’s because library authors in Go idiomatically do not add their own logging. What are the languages that do logging well? I’ve had a horrible time with Python (and I think Java but it’s been 10 years).