CodeRigor

FREE LESSON

Errors as values & useful context

Return explicit failure values, preserve error identity when wrapping, and avoid publishing partial results.

Many Go APIs return a useful value and an error. A nil error signals success; a nonnil error means the caller must follow the API's failure policy before using the other result. This is more explicit than ordinary Python exception propagation and differs from C++ stack unwinding. It also makes it easy to accidentally ignore a failure if you discard the error with _. Reserve that choice for cases where the contract really makes failure irrelevant.

The lesson's parser returns (0, err) on failure. That does not make every zero a failure: parsing the string "0" returns (0, nil). Read both results. Validate range after parsing because syntactically valid decimal text can still be outside the application's accepted domain. The standard parser also reports values too large for int; do not treat its numeric return as successful data.

Check the error before consuming the value
start / 4
Zero is meaningful on success; the separate error result identifies failure.
Check the error before consuming the value
Zero is meaningful on success; the separate error result identifies failure.

Preserve a cause while adding context

A sentinel created with errors.New can name a public failure category. A batch operation may add which item failed. Use fmt.Errorf with %w to retain the cause, and errors.Is to ask whether a wrapped error matches the category. Exact string comparison couples callers to messages intended for people. Wrapping is part of an API decision: exposing a cause lets callers depend on it.

Go
package main
import ("errors"; "fmt")
func main() {
    invalid := errors.New("invalid count")
    wrapped := fmt.Errorf("count 2: %w", invalid)
    printed := fmt.Errorf("count 2: %v", invalid)
    fmt.Println(errors.Is(wrapped, invalid), errors.Is(printed, invalid))
}

The text of the two errors looks alike, but the output is true false. Wrapping preserves an inspectable chain. Creating a new errors.New with the same message also creates a different sentinel identity.

Atomic totals do not publish a prefix
start / 2
A bad second item discards the accumulated 7 under this API contract.
Atomic totals do not publish a prefix
A bad second item discards the accumulated 7 under this API contract.

Decide whether partial work may escape

TotalCounts promises all-or-nothing output. It can accumulate internally while validating, but any failure returns zero, not the current sum. Other APIs can legitimately return partial work alongside an error; that must be stated and tested separately. Error handling is a data contract, not a universal rule to throw away every partial result in every program.

For ordinary invalid user input, return an error rather than panic. A panic indicates a different control-flow mechanism and is not the expected path for a malformed count. Library code also should not terminate the process to announce a parse error; the caller decides how to render it and what exit status a command should use.

Practice successful zero, trimmed input, a fractional string and an oversized integer. The fill preserves the error cause at the batch boundary. The bugs respectively reject a valid boundary and lose identity while retaining text. The tests inspect errors.Is and the documented position without prescribing every word of an error message.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Focus on explicit error results and caller handling. The lecture predates errors.Is and %w; the modern official error article supplies those details.