evanw/esbuild · critical
Invalid log level
Error message
Invalid log level
What it means
validateLogLevel (pkg/api/api_impl.go:204) panics when LogLevel is outside LogLevelSilent(0), LogLevelVerbose(1), LogLevelDebug(2), LogLevelInfo(3), LogLevelWarning(4), LogLevelError(5). The chosen level gates which messages the logger keeps; an out-of-range value would yield nondeterministic filtering, so esbuild aborts. The function is called from contextImpl, Transform option setup, and validateLogOverrides.
Solutions
- Set LogLevel (and every LogOverride value) via the api.LogLevel* constants only; LogLevelSilent suppresses all output.
- Clamp decoded integers to 0..5 before assigning, defaulting to LogLevelInfo when uncertain.
- Represent verbosity in serialized config as a string and map it to the constant at load.
- Re-pin esbuild to a single version across your stack.
Example fix
// before
opts := api.BuildOptions{LogLevel: api.LogLevel(11), LogOverride: map[string]api.LogLevel{"css-syntax-error": api.LogLevel(9)}}
// after
opts := api.BuildOptions{LogLevel: api.LogLevelInfo, LogOverride: map[string]api.LogLevel{"css-syntax-error": api.LogLevelWarning}} Defensive patterns
Strategy: validation
Validate before calling
func checkLogLevel(l api.LogLevel) error {
switch l {
case api.LogLevelSilent, api.LogLevelVerbose, api.LogLevelDebug,
api.LogLevelInfo, api.LogLevelWarning, api.LogLevelError:
return nil
}
return fmt.Errorf("invalid log level %d (want 0..5)", uint8(l))
}
// also check every value in LogOverride:
for k, v := range opts.LogOverride {
if err := checkLogLevel(v); err != nil { return fmt.Errorf("log override %q: %w", k, err) }
} Type guard
func isValidLogLevel(l api.LogLevel) bool {
switch l {
case api.LogLevelSilent, api.LogLevelVerbose, api.LogLevelDebug,
api.LogLevelInfo, api.LogLevelWarning, api.LogLevelError:
return true
}
return false
} Prevention
- Remember LogOverride values are also LogLevels and must be validated.
- Clamp UI-supplied verbosity integers to 0..5 before passing them in.
- Store verbosity as a string and translate at the boundary.
- Default to LogLevelInfo when unsure.
When it happens
Trigger: Passing a numeric LogLevel from a cast, JSON/protobuf decode, or an inter-language bridge whose enumeration ordering differs. Also triggered when LogOverride map values are out of range (validateLogOverrides calls validateLogLevel on each value).
Common situations: Configuration UIs that store verbosity as an integer and forget to clamp it, version skew between the JS API and Go core after a re-vendor, or downstream code that aliases a removed constant (e.g. an old `LogLevelOff`).
Related errors
AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09).
Data as JSON: /api/errors/b5604ab734b19e86.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/api/api_impl.go:204
}
}
func validateLogLevel(value LogLevel) logger.LogLevel {
switch value {
case LogLevelVerbose:
return logger.LevelVerbose
case LogLevelDebug:
return logger.LevelDebug
case LogLevelInfo:
return logger.LevelInfo
case LogLevelWarning:
return logger.LevelWarning
case LogLevelError:
return logger.LevelError
case LogLevelSilent:
return logger.LevelSilent
default:
panic("Invalid log level")
}
}
func validateLogStyle(value LogStyle) logger.LogStyle {
switch value {
case LogStyleDefault:
return logger.StyleDefault
case LogStyleClang:
return logger.StyleClang
case LogStyleVisualStudio:
return logger.StyleVisualStudio
default:
panic("Invalid log style")
}
}
func validateASCIIOnly(value Charset) bool {
switch value {View on GitHub (pinned to f6058f8364)