One distinction that is often made by core team members is that you only need to annotate errors at package boundaries, and that returning unannotated errors within a package is fine. But in most code, the packages are not so well-defined, or well-thought-out, or sacrosanct, that they represent a good proxy for annotation decisions.
I would much rather have an error with too much or duplicate annotation than one with not enough. And I would further argue that, yes, in the stdlib, errors are generally under-annotated.
Legitimate question: then what does good Go code that is written by good Go programmers do/look like? Wrap `err` in `errors.New("some function failed", err)`?
At a minimum, an error should be logged with appropriate context and execution allowed to continue; or annotated and returned. Error annotation is currently best achieved with pkg/errors as e.g. `errors.Wrap(err, "error doing thing")`. (The xerrors suggestion of `fmt.Errorf("error doing thing: %w", err)` is awkward and hacky.)
Many programs can benefit from a more structured approach to error management. But once you get past the minimum (above) there's no one-size solution for what "a more structured approach" looks like. I really enjoy how upspin.io does their errors package, though it is somewhat esoteric. I'm also reading and generally liking how Cockroach does things, though I don't like the coupling to Protobufs.
I would much rather have an error with too much or duplicate annotation than one with not enough. And I would further argue that, yes, in the stdlib, errors are generally under-annotated.