Go errors don't carry stack traces. How do you get stack information when you need it?
Question 315MediumGo 1.22 to 1.25
The standard library leaves stacks out of errors on purpose: capturing them costs time, and well-written wrap messages usually give enough context. When you do need one:
runtime/debug.Stack()/debug.PrintStack(): the current goroutine's stack, typically captured in arecoverhandler.runtime.Callers+runtime.CallersFrames: record program counters cheaply when the error is created, and turn them into symbols only when printing.GOTRACEBACK=all|system|crashcontrols how much a crash prints (crashalso produces a core dump).debug.SetTracebacksets it from code.- Third-party packages (
pkg/errors, which is archived, orcockroachdb/errors) andlog/slogwith the source option.
type stackErr struct {
err error
pcs []uintptr
}
func (e *stackErr) Error() string { return e.err.Error() }
func (e *stackErr) Unwrap() error { return e.err }
func WithStack(err error) error {
pcs := make([]uintptr, 32)
n := runtime.Callers(2, pcs) // skip Callers and WithStack
return &stackErr{err, pcs[:n]}
}
func (e *stackErr) Stack() string {
var b strings.Builder
frames := runtime.CallersFrames(e.pcs)
for {
f, more := frames.Next()
fmt.Fprintf(&b, "%s\n\t%s:%d\n", f.Function, f.File, f.Line)
if !more {
return b.String()
}
}
}
Capture a stack once, at the lowest point where the error enters your code, not at every layer.
More on Error Handling & panics
- Q313How do you propagate errors from multiple goroutines? Explain
errgroup. - Q314How do you collect all errors from concurrent workers rather than just the first?
- Q316"Handle an error only once." What does that mean, and what is wrong with log-and-return?
- Q317Why is
defer f.Close()potentially a bug when writing files? How do you handle theCloseerror? - Q318What does this print? (Shadowed
err) - Q319What does
fmt.Errorf("op: %w", err)return whenerris nil? Why does this matter in helper functions?