Fair point - this is a nasty result of their simple interface, and I think a mistake given much of the std lib doesn't use concrete types behind the interface. fmt.Errorf leads directly to this. It's simple, but not very flexible and can lead to horrible habits like parsing strings.
if .... else boilerplate
It's more like if boilerplate. People don't tend to use else unless absolutely necessary - it's if err deal with it, otherwise proceed. I agree errors in Go are more verbose than in some other languages, though in practice I don't find this a huge problem. I think I'd prefer result types but you're still handling it in a similar way.
Underscore everywhere to silence them
Abuse from panic, aka exceptions in disguise
This is not how Go is used in practice, I mean I'm sure somebody somewhere does this but it is not the norm. I maintain large codebases at work with zero instances of these faults.
I don't know why this is a thing. I create an explicit type for errors that I know I'm going to do something with, and do type assertions to check for them. I believe this was the intention behind the design in the first place.
I think that's the main problem with this design - you're reliant on others to produce errors you can reliably check against, and every library could potentially have its own error types (similar to the problems with a proliferation of exception types in languages using exceptions). When using your own libraries it is not a huge problem - as you say you can start using a more complex type.
Not sure what the answer is, but the error interface which only allows returning a string is partly to blame IMO.