nsqio/nsq · error
invalid log level '%s' (debug, info, warn, error, fatal)
Error message
invalid log level '%s' (debug, info, warn, error, fatal)
What it means
internal/lg/loglevel.go's LevelFromString (internal/lg/lg.go) parses log level strings for every nsq binary's --log-level flag; it accepts only debug, info, warn, error and fatal after lowercasing. Any other value returns 'invalid log level \'%s\' ...' listing the valid set, and the binary exits during flag validation before doing any work.
Source
Thrown at internal/lg/lg.go:73
return "FATAL"
}
return "invalid"
}
func ParseLogLevel(levelstr string) (LogLevel, error) {
switch strings.ToLower(levelstr) {
case "debug":
return DEBUG, nil
case "info":
return INFO, nil
case "warn":
return WARN, nil
case "error":
return ERROR, nil
case "fatal":
return FATAL, nil
}
return 0, fmt.Errorf("invalid log level '%s' (debug, info, warn, error, fatal)", levelstr)
}
func Logf(logger Logger, cfgLevel LogLevel, msgLevel LogLevel, f string, args ...interface{}) {
if cfgLevel > msgLevel {
return
}
_ = logger.Output(3, fmt.Sprintf(msgLevel.String()+": "+f, args...))
}
func LogFatal(prefix string, f string, args ...interface{}) {
logger := log.New(os.Stderr, prefix, log.Ldate|log.Ltime|log.Lmicroseconds)
Logf(logger, FATAL, FATAL, f, args...)
os.Exit(1)
}
View on GitHub (pinned to 85cf10c09c)
Solutions
- Use exactly one of: debug, info, warn, error, fatal (case-insensitive).
- If the level comes from an environment variable that may be unset, default it in the wrapper: --log-level=${LOG_LEVEL:-info}.
- Fix 'warning' -> 'warn' and 'err' -> 'error' in configs; these are the two most common near-misses.
Example fix
# before nsqd --log-level=warning # invalid log level 'warning' (debug, info, warn, error, fatal) # after nsqd --log-level=warn
Defensive patterns
Strategy: validation
Validate before calling
func isValidLogLevel(s string) bool {
switch strings.ToLower(s) {
case "debug", "info", "warn", "error", "fatal":
return true
}
return false
}
if lvl := os.Getenv("NSQ_LOG_LEVEL"); lvl != "" && !isValidLogLevel(lvl) {
return fmt.Errorf("NSQ_LOG_LEVEL=%q invalid; use debug|info|warn|error|fatal", lvl)
} Type guard
func isValidLogLevel(s string) bool {
switch strings.ToLower(s) {
case "debug", "info", "warn", "error", "fatal":
return true
}
return false
} Try / catch
// wrapper scripts: catch flag errors and hint the accepted set
if err := runNsqd(args); err != nil && strings.Contains(err.Error(), "invalid log level") {
return errors.New("set --log-level to one of: debug, info, warn, error, fatal (note: 'warning' is not valid)")
} Prevention
- Default env-driven levels in wrappers: ${LOG_LEVEL:-info}.
- Remember the odd one out is 'warn', not 'warning'.
- Lint configs for near-miss level names before deploy.
When it happens
Trigger: Passing --log-level=warning, --log-level=WARNING is fine (lowercased) but --log-level=err, notice, trace, INFO2, or a value with whitespace/typo (e.g. 'in fo', 'info ') fails. It applies to nsqd, nsqlookupd, nsqadmin, and the apps since they all use internal/lg via their options.
Common situations: Copy-pasting log level names from other ecosystems (python 'warning', syslog 'notice', logrus 'trace'); config templating that substitutes an empty or unset variable producing '--log-level=' (empty string is NOT in the switch, so it fails); upgrading scripts where 'warning' was tolerated by an older wrapper.
Related errors
- unknown tlsVersionOption %q
- failed to append certificate to pool
- cannot flock directory %s - %s (possibly in use by another i
- failed to LoadX509KeyPair %s, %s - %s
- failed to read TLS root CA file %s - %s
AI-assisted analysis of nsqio/nsq@85cf10c09c (2026-08-16).
Data as JSON: /api/errors/8f88b5fe71a93569.
Report an issue: GitHub.