caddyserver/caddy · error
unrecognized log level: %s
Error message
unrecognized log level: %s
What it means
After placeholder replacement and lowercasing, the level string did not match any of Caddy's accepted levels: debug, info (or empty), warn, error, panic, fatal. This is a plain bad-value error, not a placeholder-resolution error.
Source
Thrown at logging.go:774
}
level = strings.ToLower(level)
// set up the log level
switch level {
case "debug":
return zapcore.DebugLevel, nil
case "", "info":
return zapcore.InfoLevel, nil
case "warn":
return zapcore.WarnLevel, nil
case "error":
return zapcore.ErrorLevel, nil
case "panic":
return zapcore.PanicLevel, nil
case "fatal":
return zapcore.FatalLevel, nil
default:
return nil, fmt.Errorf("unrecognized log level: %s", level)
}
}
// Log returns the current default logger.
func Log() *zap.Logger {
defaultLoggerMu.RLock()
defer defaultLoggerMu.RUnlock()
return defaultLogger.logger
}
// BufferedLog sets the default logger to one that buffers
// logs before a config is loaded.
// Returns the buffered logger, the original default logger
// (for flushing on errors), and the buffer core so that the
// caller can flush the logs after the config is loaded or
// fails to load.
func BufferedLog() (*zap.Logger, *zap.Logger, *internal.LogBufferCore) {
defaultLoggerMu.Lock()View on GitHub (pinned to 50e54ee279)
Solutions
- Use one of: DEBUG, INFO, WARN, ERROR, PANIC, FATAL (case-insensitive).
- Remove the level line entirely — empty defaults to INFO.
- If you need trace-level verbosity, note Caddy has no TRACE level; use DEBUG.
Example fix
// before (caddyfile)
{
log {
level TRACE
}
}
// after
{
log {
level DEBUG
}
} Defensive patterns
Strategy: validation
Validate before calling
var allowedLevels = map[string]bool{"": true, "debug": true, "info": true, "warn": true, "error": true, "panic": true, "fatal": true}
func levelOK(s string) bool { return allowedLevels[strings.ToLower(strings.TrimSpace(s))] } Try / catch
if err := caddy.Validate(cfg); err != nil {
if strings.Contains(err.Error(), "unrecognized log level") {
// replace with debug/info/warn/error/panic/fatal
}
return err
} Prevention
- Restrict level pickers/dropdowns in tooling to the six accepted values.
- Remember there is no TRACE/NOTICE/VERBOSE in Caddy.
- Case does not matter, spelling does.
When it happens
Trigger: level set to 'trace', 'verbose', 'notice', 'WARNING' with a typo like 'warnning', or any string not in the accepted set.
Common situations: Porting configs from other servers whose level names differ (nginx's 'warn' works, but syslog's 'notice' does not); assuming 'trace' exists because OpenTelemetry or zap docs mention it; case is fine (it is lowercased) but spelling must match exactly.
Related errors
- invalid action type
- include and exclude must not intersect, but found %s in both
- no identifiers configured
- loading log encoder module: %v
- when both include and exclude are populated, each element mu
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/45fca6cadf174838.
Report an issue: GitHub.