stalwartlabs/stalwart · error
Invalid LOG level
Error message
Invalid LOG level
What it means
This panic comes from `Level::from_str(&level).expect("Invalid LOG level")` during telemetry/tracer setup in `parse`. The `LOG` environment variable is read only when the `dev_mode` feature is enabled, and it must parse into a trc `Level` via `Level::from_str`. That parser (crates/trc/src/event/level.rs:44) only accepts the case-insensitive strings "disable", "trace", "debug", "info", "warn", and "error"; anything else makes `from_str` return `Err` and the `expect` panics with "Invalid LOG level", aborting configuration parsing.
Source
Thrown at crates/common/src/config/telemetry.rs:530
// Parse webhook events
apply_events(hook.events, hook.events_policy, |event_type| {
if event_type != EventType::Telemetry(TelemetryEvent::WebhookError) {
tracer.interests.set(event_type);
global_interests.set(event_type);
}
});
if !tracer.interests.is_empty() {
tracers.push(tracer);
} else {
bp.build_error(id, "No events enabled for webhook");
}
}
#[cfg(feature = "dev_mode")]
if let Ok(level) = std::env::var("LOG") {
let level = Level::from_str(&level).expect("Invalid LOG level");
for event_type in EventType::variants() {
let event_level = custom_levels
.get(event_type)
.copied()
.unwrap_or(event_type.level());
if level.is_contained(event_level) {
global_interests.set(event_type.to_id() as usize);
}
}
tracers.push(TelemetrySubscriber {
id: "default".to_string(),
interests: global_interests.clone(),
typ: TelemetrySubscriberType::ConsoleTracer(ConsoleTracer {
ansi: true,
multiline: false,
buffered: true,
}),View on GitHub (pinned to e962003857)
Solutions
- Set LOG to one of the exact accepted values (case-insensitive): disable, trace, debug, info, warn, or error — e.g. LOG=debug.
- Remove RUST_LOG-style per-module filters; a single level token is the only accepted form.
- Trim whitespace from the value: export LOG="$(echo "$LOG" | tr -d '[:space:]')" or fix the Dockerfile/systemd unit/export that introduces it.
- If you meant per-event granularity, use the config's event log level settings (custom_levels) instead of the LOG env var.
- If you are not intentionally running a dev_mode build, unset LOG entirely — the var is only read when the dev_mode feature is compiled in.
Example fix
// before LOG=warning stalwart-mailserver # panics: Invalid LOG level // after LOG=warn stalwart-mailserver
Defensive patterns
Strategy: validation
Validate before calling
const VALID_LEVELS: [&str; 6] = ["disable", "trace", "debug", "info", "warn", "error"];
let log = std::env::var("LOG").unwrap_or_default();
assert!(VALID_LEVELS.contains(&log.to_ascii_lowercase().as_str()), "LOG must be one of {:?}, got {:?}", VALID_LEVELS, log); Type guard
fn is_valid_log_level(s: &str) -> bool {
matches!(s.to_ascii_lowercase().as_str(), "disable" | "trace" | "debug" | "info" | "warn" | "error")
} Prevention
- Never export a RUST_LOG-style filter string in LOG; it accepts a single level token only.
- Use "warn", not "warning"; use "error", not "critical".
- Trim whitespace when setting env vars in Dockerfiles/systemd units.
- In production builds (no dev_mode), configure log levels via config file settings instead of the LOG env var.
When it happens
Trigger: Setting the LOG env var (with the binary built with the dev_mode feature) to anything outside {disable, trace, debug, info, warn, error} — e.g. LOG=verbose, LOG=5, LOG=warning, LOG=trace,debug (a comma-separated RUST_LOG-style filter is NOT accepted), or a value with surrounding whitespace/newline.
Common situations: Developers copying RUST_LOG-style syntax ("info,foo=debug") from other Rust projects; typing "warning" or "critical" instead of "warn"/"error"; numeric log levels; exporting LOG="info " with a trailing space in a shell profile or Docker ENV; running a dev_mode build in an environment where LOG was set for an unrelated tool.
Understand the failure class
Background: "is not a valid" / "Invalid ... value" environment variable errors: how libraries validate env vars and what to do when they reject yours — this error's family across 48 libraries.
Related errors
- unwrap_tls called on non-TLS acceptor
- Migration aborted: {message}
- StoreEvent::HttpStoreError
- journald does not exist in this environment
- journald not supported on non-Unix
AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06).
Data as JSON: /api/errors/ddeed222449cb40c.
Report an issue: GitHub.