uber-go/zap · error
unrecognized level: %q
Error message
unrecognized level: %q
What it means
Level.UnmarshalText parses textual level names ("debug", "info", "warn", "error", "dpanic", "panic", "fatal", case-insensitively). If the text (and its lowercase form) matches none of them, it returns "unrecognized level: %q". The nil-receiver case is reported separately as errUnmarshalNilLevel.
Source
Thrown at zapcore/level.go:175
// MarshalText marshals the Level to text. Note that the text representation
// drops the -Level suffix (see example).
func (l Level) MarshalText() ([]byte, error) {
return []byte(l.String()), nil
}
// UnmarshalText unmarshals text to a level. Like MarshalText, UnmarshalText
// expects the text representation of a Level to drop the -Level suffix (see
// example).
//
// In particular, this makes it easy to configure logging levels using YAML,
// TOML, or JSON files.
func (l *Level) UnmarshalText(text []byte) error {
if l == nil {
return errUnmarshalNilLevel
}
if !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) {
return fmt.Errorf("unrecognized level: %q", text)
}
return nil
}
func (l *Level) unmarshalText(text []byte) bool {
switch string(text) {
case "debug":
*l = DebugLevel
case "info", "": // make the zero value useful
*l = InfoLevel
case "warn", "warning":
*l = WarnLevel
case "error":
*l = ErrorLevel
case "dpanic":
*l = DPanicLevel
case "panic":
*l = PanicLevelView on GitHub (pinned to bbd4ecbd87)
Solutions
- Use one of the exact level names: debug, info, warn, error, dpanic, panic, fatal (case-insensitive).
- Trim and validate the level string before parsing, e.g. strings.TrimSpace(strings.ToLower(os.Getenv("LOG_LEVEL"))).
- Prefer zapcore.ParseLevel with a fallback: on error, log a warning and default to info.
- If you need a custom name like "trace", map it in your config layer to a real zap level.
Example fix
// before
level, _ := zapcore.ParseLevel(cfg.Level) // "verbose" -> unrecognized level
// after
level, err := zapcore.ParseLevel(cfg.Level)
if err != nil {
level = zapcore.InfoLevel
} Defensive patterns
Strategy: validation
Validate before calling
var validLevels = map[string]bool{
"debug": true, "info": true, "warn": true,
"error": true, "dpanic": true, "panic": true, "fatal": true,
}
lvl := strings.TrimSpace(strings.ToLower(os.Getenv("LOG_LEVEL")))
if !validLevels[lvl] {
lvl = "info"
}
level, err := zapcore.ParseLevel(lvl) Try / catch
level, err := zapcore.ParseLevel(raw)
if err != nil {
log.Printf("invalid LOG_LEVEL %q, defaulting to info", raw)
level = zapcore.InfoLevel
} Prevention
- Validate level strings from config/env against the allowed set before parsing.
- Always handle the error from ParseLevel/UnmarshalText — never discard it.
- Document the exact accepted level names in your configuration reference.
When it happens
Trigger: Unmarshaling a config value (YAML/JSON/TOML) or calling zapcore.ParseLevel/Set with a string that is not a valid level name, e.g. ParseLevel("verbose"), "WARNING" typo variants like "warnning", or empty string.
Common situations: Config files with misspelled or nonstandard level names ("trace", "verbose", "WARN "+trailing whitespace), environment-variable-driven levels where users supply arbitrary strings, older zap versions lacking "notice"/custom levels.
Related errors
- invalid increase level, as level %q is allowed by increased
- can't unmarshal a nil *Level
- unrecognized level: %q
- can't parse %q as a URL: %v
AI-assisted analysis of uber-go/zap@bbd4ecbd87 (2026-08-31).
Data as JSON: /api/errors/6861988b8998e5a1.
Report an issue: GitHub.