uber-go/zap · critical
underlying error from the wrapped (*Logger, error) call
Error message
underlying error from the wrapped (*Logger, error) call
What it means
zap.Must panics with the error returned by a (*Logger, error) constructor (e.g. zap.NewProduction or a Config.Build) if that error is non-nil. The panic value is the underlying error itself, so the runtime message shows the wrapped cause of logger construction failure.
Source
Thrown at logger.go:119
return NewProductionConfig().Build(options...)
}
// NewDevelopment builds a development Logger that writes DebugLevel and above
// logs to standard error in a human-friendly format.
//
// It's a shortcut for NewDevelopmentConfig().Build(...Option).
func NewDevelopment(options ...Option) (*Logger, error) {
return NewDevelopmentConfig().Build(options...)
}
// Must is a helper that wraps a call to a function returning (*Logger, error)
// and panics if the error is non-nil. It is intended for use in variable
// initialization such as:
//
// var logger = zap.Must(zap.NewProduction())
func Must(logger *Logger, err error) *Logger {
if err != nil {
panic(err)
}
return logger
}
// NewExample builds a Logger that's designed for use in zap's testable
// examples. It writes DebugLevel and above logs to standard out as JSON, but
// omits the timestamp and calling function to keep example output
// short and deterministic.
func NewExample(options ...Option) *Logger {
encoderCfg := zapcore.EncoderConfig{
MessageKey: "msg",
LevelKey: "level",
NameKey: "logger",
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.StringDurationEncoder,
}View on GitHub (pinned to bbd4ecbd87)
Solutions
- Inspect the panic message for the wrapped cause (e.g. "open sink ...: permission denied") and fix the config/path.
- If construction may legitimately fail, use the (logger, err) form instead of Must and handle the error, falling back to zap.NewNop().
- Ensure output directories exist and are writable before the logger is constructed.
- Validate the Config (level names, encodings, paths) before calling Build.
Example fix
// before
var logger = zap.Must(zap.NewProduction()) // panics on failure
// after
logger, err := zap.NewProduction()
if err != nil {
logger = zap.NewNop()
} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate config before Must-style construction:
for _, p := range cfg.OutputPaths {
if p != "stderr" && p != "stdout" {
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil { return err }
}
}
if _, err := zapcore.ParseLevel(cfg.Level.Level().String()); err != nil { return err } Try / catch
func initLogger() *zap.Logger {
l, err := zap.NewProduction()
if err != nil {
fmt.Fprintf(os.Stderr, "logger init failed: %v\n", err)
return zap.NewNop()
}
return l
}
var logger = initLogger() Prevention
- Avoid zap.Must in package-level vars for deploy targets with uncertain filesystem permissions.
- Verify output paths are writable as the runtime user (especially in containers).
- Validate the entire Config (levels, encodings, paths) before Build().
- Keep a fallback (Nop or stderr) logger so the process can still start.
When it happens
Trigger: Package-level var logger = zap.Must(zap.NewProduction()) (or zap.Must(cfg.Build())) when construction fails: invalid config paths, unreadable output files, invalid encoding names, unparseable level strings in the config.
Common situations: Program startup crashing at var-initialization time because the log file path is unwritable in a container, or a config with an invalid OutputPaths/Level value; commonly seen when deploying to restricted environments where /var/log isn't writable.
Related errors
AI-assisted analysis of uber-go/zap@bbd4ecbd87 (2026-08-31).
Data as JSON: /api/errors/a4c17417c721a874.
Report an issue: GitHub.