uber-go/zap · error
unrecognized level: %q
Error message
unrecognized level: %q
What it means
levelToFunc in global.go maps a zapcore.Level string (via levelToFunc's caller's parsed level) to the corresponding logger method for redirecting std log output. When the supplied level string does not match any known zap level (Debug, Info, Warn, Error, DPanic, Panic, Fatal), it returns this error. It means the level value passed to NewStdLogAt or redirectStdLogAt was not a recognized zap level.
Source
Thrown at global.go:158
func levelToFunc(logger *Logger, lvl zapcore.Level) (func(string, ...Field), error) {
switch lvl {
case DebugLevel:
return logger.Debug, nil
case InfoLevel:
return logger.Info, nil
case WarnLevel:
return logger.Warn, nil
case ErrorLevel:
return logger.Error, nil
case DPanicLevel:
return logger.DPanic, nil
case PanicLevel:
return logger.Panic, nil
case FatalLevel:
return logger.Fatal, nil
}
return nil, fmt.Errorf("unrecognized level: %q", lvl)
}
type loggerWriter struct {
logFunc func(msg string, fields ...Field)
}
func (l *loggerWriter) Write(p []byte) (int, error) {
p = bytes.TrimSpace(p)
l.logFunc(string(p))
return len(p), nil
}
View on GitHub (pinned to bbd4ecbd87)
Solutions
- Use one of the recognized level strings: debug, info, warn, error, dpanic, panic, fatal.
- Normalize user/config input with zapcore.ParseLevel or strings.ToLower before passing to NewStdLogAt/RedirectStdLogAt.
- Handle the returned error by falling back to a default level (e.g. InfoLevel) instead of failing startup.
Example fix
// before
logger, err := zap.NewStdLogAt(zapLog, "verbose")
// after
lvl, perr := zapcore.ParseLevel("verbose") // still fails; validate input first
if perr != nil {
lvl = zapcore.InfoLevel
}
logger, err := zap.NewStdLogAt(zapLog, lvl.String()) Defensive patterns
Strategy: validation
Validate before calling
func validZapLevel(s string) bool {
switch strings.ToLower(strings.TrimSpace(s)) {
case "debug", "info", "warn", "error", "dpanic", "panic", "fatal":
return true
}
return false
} Try / catch
lvl, err := zapcore.ParseLevel(cfg.Level)
if err != nil {
lvl = zapcore.InfoLevel // fallback
}
stdLog, err := zap.NewStdLogAt(zapLog, lvl) Prevention
- Always build levels via zapcore.ParseLevel rather than raw strings
- Normalize case and trim whitespace from config/env level values
- Add a startup-time config validation step that rejects unknown levels early
When it happens
Trigger: Calling zap.NewStdLogAt(l, level) or zap.RedirectStdLogAt(l, level) with a level string that is not one of debug/info/warn/error/dpanic/panic/fatal (case variations or typos like 'trace' or 'verbose' are rejected).
Common situations: Level names read from environment variables or config files that use non-zap naming conventions (e.g. 'trace', 'verbose', 'notice', or uppercase 'DEBUG' if parsed case-sensitively by the caller), or typos in programmatic level constants.
Related errors
- can't parse %q as a URL: %v
- missing Level
- no encoder name specified
- missing EncodeTime in EncoderConfig
- must specify logging level
AI-assisted analysis of uber-go/zap@bbd4ecbd87 (2026-08-31).
Data as JSON: /api/errors/58803efed54eabc7.
Report an issue: GitHub.