Backend Development 13 min read

Go Error Handling Practices in ZuanZuan Operations: Usage and Management

This article explains how ZuanZuan's operations team applies Go's error handling mechanisms—including error values, panic/recover, sentinel errors, and custom error types—along with practical guidelines and code examples to improve reliability and maintainability of production services.

Zhuanzhuan Tech
Zhuanzhuan Tech
Zhuanzhuan Tech
Go Error Handling Practices in ZuanZuan Operations: Usage and Management

In production projects, Go's error values provide a clear way to locate issues, but unlike Java's try...catch or Python's try...except , Go relies on multiple return values and propagates error upward through function calls. This article shares the ZuanZuan operations team's practices for using and handling errors in Go.

Error vs Exception

Advantages of using errors instead of exceptions

Simple and controllable; handling each function's possible error avoids semantic confusion of exception variables.

Focuses on failure rather than success, emphasizing error handling.

Maintains linear program flow without hidden control flow.

Drawbacks of using errors

Every function that returns an error must handle it, leading to verbose code.

Propagating error upward can cause duplicate handling and noisy logs.

Misuse of panic or ignoring recover reduces program robustness.

panic and recover

In Go, a "panic" is the language's notion of an exception. A panic terminates the program unless recovered. Panics are typically thrown in two scenarios: unexpected library or data structure failures, and explicit developer‑initiated panics during initialization.

panic usage scenarios

Panics should rarely be thrown manually; they are appropriate during critical initialization failures (e.g., missing log configuration, failed database connection) where the program cannot continue.

Another scenario is configuration validation: if a config value is obviously invalid (e.g., an absurd port number), a panic can prevent obscure runtime errors.

In summary, panics are reserved for unrecoverable errors that occur outside normal program flow.

recover usage scenarios

The recover keyword catches a panic, acting as the final safeguard. Typical uses include:

Recovering from panics in the main HTTP server to avoid total process termination and allow a supervisor (systemd, container runtime) to restart the service.

Recovering within worker goroutines so that a panic in one worker does not crash the entire application; the goroutine can be restarted.

Generally, recover is applied in both the main goroutine and auxiliary goroutines to quickly restore crashed execution.

Custom Error

Sentinel Error

Sentinel errors are predefined variables (e.g., io.EOF ) that callers compare with == . While simple, they are inflexible because they prevent attaching additional context and can create tight coupling between packages.

var ErrShortWrite = errors.New("short write")
var errInvalidWrite = errors.New("invalid write result")
var ErrShortBuffer = errors.New("short buffer")
var EOF = errors.New("EOF")
var ErrUnexpectedEOF = errors.New("unexpected EOF")

Because sentinel errors require equality checks, they are less suitable when richer error information is needed.

Hiding Internal Details

The standard error interface is simple:

type error interface {
    Error() string
}

Custom error types can implement additional methods, such as the net.Error interface with Timeout() and Temporary() methods:

package net

type Error interface {
    error
    Timeout() bool   // is the error a timeout?
    Temporary() bool // is the error transient and retry‑able?
}

Example of checking a temporary error:

if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
    time.Sleep(1e9)
    continue
}
if err != nil {
    log.Fatal(err)
}

A helper to detect temporary errors:

type temporary interface {
    Temporary() bool
}

func IsTemporary(err error) bool {
    temp, ok := err.(temporary)
    return ok && temp.Temporary()
}

Error Handling Principles

Principle 1: Keep the normal code path linear

Check errors early ( if err != nil { … } ) and handle them immediately, keeping the main flow free of nested indentation.

// Recommended style
f, err := os.Open(path)
if err != nil {
    // handle error
}
// normal workflow

Principle 2: Do not ignore errors, and avoid handling the same error multiple times

Every error that can affect service stability should be handled exactly once; logging counts as handling.

Principle 3: Wrap errors to provide richer context

Using packages like github.com/pkg/errors , wrap errors with errors.Wrap to retain stack traces and context, and retrieve the root cause with errors.Cause . Log the error only once.

Principle 4: Once an error is handled, it is no longer an error

After processing, the error should not be returned or reported again.

Summary

Go's error design follows two core ideas:

Errors Are Values : any type implementing Error() can be used as an error, enabling flexible definitions.

Handle All Potential Errors : Go encourages explicit error handling rather than hidden exceptions, improving program robustness.

The ZuanZuan operations team distilled these principles into practical guidelines for most error scenarios, acknowledging that no single approach fits all cases and that trade‑offs must be considered based on business needs.

About the Author

Lü Rui, ZuanZuan Operations Engineer, responsible for cloud computing and cloud‑native operations and development.

Backend DevelopmentGoError Handlingpanicsentinel errorrecover
Zhuanzhuan Tech
Written by

Zhuanzhuan Tech

A platform for Zhuanzhuan R&D and industry peers to learn and exchange technology, regularly sharing frontline experience and cutting‑edge topics. We welcome practical discussions and sharing; contact waterystone with any questions.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.