Go

"Handle an error only once." What does that mean, and what is wrong with log-and-return?

Question 316MediumGo 1.22 to 1.25

Each error should be handled once. Handling means logging it, recovering from it (retry, fallback, default value), or turning it into a response. The alternative is to return it, usually wrapped with context. If you log and also return, every layer logs the same failure, the logs fill with duplicates that have partial context, and it becomes hard to find the real cause.

// Bad: logged here, and again by every caller
func loadUser(id int) (*User, error) {
	u, err := db.Get(id)
	if err != nil {
		log.Printf("db get failed: %v", err)
		return nil, err
	}
	return u, nil
}

// Good: add context, let the top level decide
func loadUser(id int) (*User, error) {
	u, err := db.Get(id)
	if err != nil {
		return nil, fmt.Errorf("load user %d: %w", id, err)
	}
	return u, nil
}

// Top level (handler/main) handles it exactly once
if err != nil {
	slog.Error("request failed", "err", err, "path", r.URL.Path)
	http.Error(w, "internal error", http.StatusInternalServerError)
}

Deliberately ignoring an error is also a form of handling. Write _ = f.Close() with a comment so the choice is visible, rather than dropping the error silently.

More on Error Handling & panics

All 37 Error Handling & panics questions