Go's error handling is verbose but explicit. The cost is real; the benefit is also real: every error is handled where it's returned. You cannot accidentally ignore an error without making it look deliberate.

The pattern everyone reaches for: errors.Is for sentinels, errors.As for type assertions, fmt.Errorf("... %w", err) for wrapping. The wrapped error chain is inspectable without depending on string matching.

The missing piece: no stack traces by default. For production debugging, either log context at the call site or use a library that captures them. pkg/errors is the historical choice; the stdlib errors package now covers most use cases.

See: debugging-as-search, @dave / on-abstraction