Explain Go's error handling pattern. When should you use panic and recover?
go-mid-001
Your answer
Answer as you would in a real interview — explain your thinking, not just the conclusion.
Model answer
Go treats errors as values: functions return (result, error) and callers explicitly check and handle each error. This makes error paths visible in code rather than invisible exception propagation. Use fmt.Errorf with %w to wrap errors so callers can unwrap with errors.Is and errors.As. panic is reserved for programmer errors — index out of bounds, nil dereference — situations where the program state is genuinely unrecoverable. recover is used in framework code such as HTTP server middleware to catch a panicking goroutine and return a 500 without crashing the whole process. In application code you should almost never call panic; always return an error instead.
Code example
func openConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
// Wrap the error — caller can use errors.Is(err, fs.ErrNotExist)
return nil, fmt.Errorf("openConfig %q: %w", path, err)
}
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("openConfig %q: parse: %w", path, err)
}
return &cfg, nil
}
// Recover in HTTP middleware
func recoverMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if rec := recover(); rec != nil {
http.Error(w, "internal error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
Follow-up
What is the difference between fmt.Errorf with %w and errors.New? Why does wrapping matter for callers?