nautechsystems/nautilus_trader · error · anyhow::Error

Invalid log level: {v}

Error message

Invalid log level: {v}

What it means

`parse_level` (crates/common/src/logging/config.rs) converts a spec string into a tracing LevelFilter using FromStr, and maps any parse failure to this error. It exists so bad log-level strings in env vars or config specs surface a clear message naming the offending value.

Source

Thrown at crates/common/src/logging/config.rs:296

    /// # Errors
    ///
    /// Returns an error if the variable is unset or contains invalid syntax.
    pub fn from_env() -> anyhow::Result<Self> {
        let spec = env::var("NAUTILUS_LOG")?;
        Self::from_spec(&spec)
    }
}

/// Parses a boolean value from a string.
///
/// Returns `true` unless the value is explicitly "false", "0", or "no" (case-insensitive).
fn parse_bool_value(v: &str) -> bool {
    !matches!(v.to_lowercase().as_str(), "false" | "0" | "no")
}

/// Parses a log level from a string.
fn parse_level(v: &str) -> anyhow::Result<LevelFilter> {
    LevelFilter::from_str(v).map_err(|_| anyhow::anyhow!("Invalid log level: {v}"))
}

#[cfg(test)]
mod tests {
    use rstest::rstest;

    use super::*;
    use crate::config::ConfigError;

    #[rstest]
    fn test_zero_rotation_max_file_size_rejected() {
        let file_config = FileWriterConfig::new(None, None, None, Some((0, 5)));
        let result = LoggerConfig::builder().file_config(file_config).build();
        assert!(
            matches!(result, Err(ConfigError::Range { field, .. }) if field == "file_config.file_rotate.max_file_size")
        );
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Use one of the accepted levels: trace, debug, info, warn, error, off (case-insensitive).
  2. Map Python-style names before passing: WARNING->WARN, CRITICAL->ERROR.
  3. Trim whitespace/quotes from the env/config value and check spec separators (';' component=LEVEL pairs).
  4. Log or echo the failing spec value and validate at startup before launching the node.

Example fix

// before
NAUTILUS_LOG="warning"  // parse_level fails
// after
NAUTILUS_LOG="warn"     // or map: let level = match s {"WARNING"=>"WARN", s=>s};
Defensive patterns

Strategy: validation

Validate before calling

// Rust: validate level before building the config
const LEVELS: [&str; 6] = ["trace","debug","info","warn","error","off"];
assert!(LEVELS.contains(&level.to_lowercase().as_str()), "invalid level {level}");

Type guard

fn parse_level_or(v: &str, default: LevelFilter) -> LevelFilter {
    LevelFilter::from_str(v).unwrap_or(default)
}

Try / catch

match parse_level(spec) {
    Ok(l) => l,
    Err(e) => { eprintln!("fix NAUTILUS_LOG: {e}; use trace|debug|info|warn|error|off"); std::process::exit(2); }
}

Prevention

When it happens

Trigger: Passing a level string that is not one of TRACE/DEBUG/INFO/WARN/ERROR/OFF (case accepted by tracing, plus lowercase variants) to the logging spec — e.g. NAUTILUS_LOG levels segment set to "verbose", "warning", "LOG_INFO", "" or "info;" with stray punctuation.

Common situations: Copying Python logging level names ("WARNING", "CRITICAL") or syslog names into the spec; typos like "DEBG"; env var containing whitespace or quotes from shell escaping; a component name accidentally parsed as a level in the spec string.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/dbbc100ea8f36e9b. Report an issue: GitHub.