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

  1. Set LogLevel (and every LogOverride value) via the api.LogLevel* constants only; LogLevelSilent suppresses all output.
  2. Clamp decoded integers to 0..5 before assigning, defaulting to LogLevelInfo when uncertain.
  3. Represent verbosity in serialized config as a string and map it to the constant at load.
  4. 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

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)