skip to content

Errors and Failure Design

Go has no exceptions: a function returns an error value and the caller decides what happens next. Constructing, classifying, wrapping and refusing to return one are all API design here.

part ofGo (Golang)overview, primer and where to startread it →
on this pageshow

explore

questions

81 · 5 sections

In Go, what does `defer f.Close()` on a file you wrote to silently discard?

level: juniorimportance: must knowfreq 58%
basics
~20 s

Close returns an error and a deferred call discards it. On a file you wrote to, that error can be the only report that your data never reached the disk, so a failed write ends up looking like a success.

open as a page

What does Go's built-in error interface require a type to implement?

level: juniorimportance: must knowfreq 85%
basics
~20 s

error is a predeclared interface with exactly one method, Error() string. Any type that declares that method - a struct, a named int, anything - is an error and can be returned wherever error is expected.

open as a page

Why is logging an error with slog.Error and also returning it to the caller a problem?

level: juniorimportance: must knowfreq 60%
basics
~20 s

Logging and returning reports one failure twice: the function writes a log record, then whoever called it reports the same failure again. Choose one role per error - either handle it and log it, or return it and stay silent.

open as a page

Why does a Go function that can fail return (result, error), and what must the caller do first?

level: juniorimportance: must knowfreq 85%
basics
~20 s

Go reports failure as an ordinary value returned last, beside the real result. The caller tests err != nil before touching the other result, because on failure that result is only a zero value, not data.

open as a page

Why does Go style require errors.New strings to start lowercase and end without punctuation?

level: juniorimportance: must knowfreq 66%
basics
~10 s

An error string is rarely printed alone. Callers wrap it, so it lands in the middle of a longer message. Lowercase text with no trailing period or newline concatenates cleanly into one readable line.

open as a page

What is a sentinel error in Go, and how do you declare one so callers can detect it?

level: juniorimportance: must knowfreq 70%
basics
~20 s

A sentinel error is one package-level error value, such as var ErrNotFound = errors.New("not found"), that a package returns for a specific condition. Callers detect that condition by matching a returned error against that exported value.

open as a page

How do you define a custom Go error type that carries structured fields a caller can read?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Declare a struct holding the data and give it an Error() string method, normally on a pointer receiver. Any type with that method is usable as an error, and callers read the exported fields off the concrete value.

open as a page

When should a Go package export a sentinel error value rather than an exported error type?

level: middleimportance: must knowfreq 68%
basics
~20 s

Export a sentinel value when the caller only needs a yes-or-no branch, and an error type when the caller needs data to act on. A sentinel commits the package to one identity; a type commits every exported field forever.

open as a page

How should a Go package carry retryability inside the error value it returns?

level: middleimportance: must knowfreq 62%
basics
~20 s

Put the classification in the error itself: an exported sentinel matched with errors.Is, or an error type with a method such as Retryable() bool found with errors.As. Both survive wrapping; a second bool return and message text do not.

open as a page

Why should a caller use errors.Is(err, io.EOF) instead of comparing err == io.EOF?

level: middleimportance: must knowfreq 78%
basics
~20 s

The == operator only tests the outermost error value. If any layer in between returned a new error carrying the original as its cause, == stops matching. errors.Is walks that chain of causes and matches any error in it.

open as a page

What does adding an Is(target error) bool method to your own error type change about errors.Is?

level: juniorimportance: must knowfreq 42%
basics
~20 s

An Is method lets errors.Is treat your error as equivalent to a target it does not equal by value. While searching, errors.Is calls your Is method at your link and accepts a true answer as a match.

open as a page

What does errors.Join return when some arguments are nil, and what is its Error text?

level: juniorimportance: must knowfreq 52%
basics
~20 s

errors.Join skips every nil argument and returns a plain nil if all of them are nil. Otherwise it returns one error whose message is the non-nil errors' messages joined by newlines, one per line, with no trailing newline.

open as a page

In fmt.Errorf, what does the %w verb do that %v does not?

level: juniorimportance: must knowfreq 85%
basics
~20 s

%w stores the original error inside the new one, giving the result an Unwrap method so errors.Is and errors.As can still find the cause. %v only copies the error's text, so that link is gone.

open as a page

How do errors.Is and errors.As walk the chain that fmt.Errorf's %w builds?

level: middleimportance: must knowfreq 70%
basics
~20 s

Both call Unwrap repeatedly. errors.Is compares each error in the chain to the target with ==; errors.As tries to assign each one into the pointer target's type and returns true at the first that fits.

open as a page

A Go handler writes err.Error() into its 500 body and customers see the SQL and the connection string. How do you fix the error path?

level: seniorimportance: must knowfreq 52%
basics
~10 s

Stop rendering err.Error() to callers. At one boundary helper, log the whole wrapped chain with a correlation id and write a fixed message plus that id. Caller-facing wording must be carried explicitly, never inherited.

open as a page

How do you stop a panic from escaping a Go function and return it to the caller as an error?

level: juniorimportance: must knowfreq 68%
basics
~20 s

Give the function a named error result and defer a closure that calls recover. When recover returns non-nil, the closure assigns a wrapped error to that named result, so the caller sees an ordinary error instead of a panic.

open as a page

In Go, when should a function panic instead of returning an error?

level: juniorimportance: must knowfreq 80%
basics
~20 s

Panic only for bugs the program cannot continue past: impossible internal states, broken invariants, or setup at package init. Anything a caller could reasonably hit at runtime, including bad input, missing files and I/O failure, returns an error.

open as a page

What happens when two goroutines write to the same Go map without a lock?

level: juniorimportance: must knowfreq 66%
basics
~20 s

Go's built-in map is not safe for concurrent writes. The runtime notices the overlapping write and aborts the whole process with 'fatal error: concurrent map writes'. It is not a panic and the program does not continue past it.

open as a page

What does Go's built-in recover() do, and where must it be called to stop a panic?

level: juniorimportance: must knowfreq 80%
basics
~10 s

recover() stops a panicking goroutine from dying and returns the value that was passed to panic. It works only when called directly inside a function that was deferred; called anywhere else it returns nil.

open as a page

Where must the deferred recover live when a controller runs each reconcile in its own goroutine?

level: middleimportance: must knowfreq 60%
basics
~20 s

Inside the goroutine's own function, at the top, not in the code that started it. A recovery only takes effect in a function deferred by the goroutine that panicked, and a panic that escapes any goroutine terminates the whole process.

open as a page

What does ctx.Err() return after a context is cancelled versus after its deadline passes?

level: juniorimportance: must knowfreq 72%
basics
~10 s

ctx.Err() returns nil while the context is still live, context.Canceled once a cancel function has been called, and context.DeadlineExceeded once the context's deadline has passed. Match them with errors.Is, not with ==.

open as a page

Why is io.EOF returned by a Read call treated as a normal end of input, not a failure?

level: juniorimportance: must knowfreq 82%
basics
~20 s

io.EOF is a sentinel error the io.Reader contract uses to say the stream is finished, so nothing went wrong. Callers detect it, stop reading and return success; they never log it or pass it on as a fault.

open as a page

Given an error from os.Open, how do you check that the file does not exist?

level: juniorimportance: must knowfreq 76%
basics
~10 s

Use errors.Is(err, fs.ErrNotExist). os.Open returns a *fs.PathError wrapping the operating system's error, so comparing the returned error directly against fs.ErrNotExist is always false; errors.Is unwraps the chain and finds the sentinel.

open as a page

Why can an io.Reader's Read return n > 0 bytes and io.EOF in the same call?

level: middleimportance: must knowfreq 58%
basics
~20 s

The io.Reader contract lets a reader that hits the end of its data while filling your buffer report both at once. Process the n bytes returned before you inspect the error, or the last chunk of the stream is silently dropped.

open as a page

Why doesn't errors.Is(err, context.DeadlineExceeded) match an expired socket read deadline?

level: middleimportance: should knowfreq 44%
basics
~10 s

They are two unrelated sentinel values. A read that fails because an I/O deadline expired wraps os.ErrDeadlineExceeded; only a finished context.Context yields context.DeadlineExceeded. Both report Timeout() true through the net.Error interface.

open as a page