Can A Function Return Multiple Errors
Fast recall
Quick revision
Core answer
Practical understanding of Multiple Return Error.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
A Go function returns one error value — but that one value can represent several failures. Before Go 1.20 you handled this with fmt.Errorf and %w, which wraps exactly one cause; truly parallel failures (this batch item 3 failed AND item 7 failed) needed a custom multi-error type.
Go 1.20 standardized it: errors.Join takes any number of errors and returns a single error that reports all of them, unwraps to each, and matches each with errors.Is. The joined error's message is the components' messages joined with newlines.
var errs []error
for _, t := range tasks {
if err := t.Run(); err != nil {
errs = append(errs, fmt.Errorf("task %s: %w", t.Name, err))
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}The caller still sees ONE error from the function — the convention holds — but errors.Is can find any component cause, and the printed message includes every failure. The batch pattern (run everything, report everything that failed, not just the first) is the main use: fail-fast wastes the information about what else would have failed.
Before 1.20 and in code that must support older versions, the same shape is built by hand: a small struct with an Error() method that concatenates, and an Unwrap() []error method — which is exactly what Join provides for you now.
Can A Function Return Multiple Errors
Direct Answer
Idiomatically one. Go 1.20's errors.Join(err...) wraps several into a single error that errors.Is-matches each component.
Deep Dive & Architecture
Why was this added to the standard library in 2023 after a decade of one-error returns? Because the batch pattern had converged on private reimplementations. Everyone running parallel validation — form field checks, linters, CI steps, fan-out requests — hit the same wall: the first error is a subset of the truth, and swallowing the rest makes the caller iterate one-fix-one-round-trip. The multi-error structure shows up in enough places that the pattern deserved one shared implementation with correct Is/As semantics (a hand-rolled version usually gets the unwrap walk wrong).
The semantics that make Join correct: errors.Is(joinedErr, anyComponent) is true for each component; errors.As extracts each component type; the message joins all. The subtle part is nil handling — Join filters out nil components, and Join() with all-nils returns nil, so the builder pattern above doesn't need a nil guard.
The remaining guidance: one error return is still the default. Join is for genuinely parallel, independent failures. Sequential code (open file, parse it, save it) should still return the first error — the later steps didn't run, so reporting them as failures would be fiction.
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is Multiple Return Error, and when would you use it?
- When would you use Multiple Return Error in a real project?
- How would you compare Multiple Return Error with an alternative?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.