{"record":{"id":"d5894a187222ce63","repo":"nautechsystems/nautilus_trader","slug":"component-log-level-for-key-must-be-a-string","errorCode":null,"errorMessage":"Component log level for '{key}' must be a string, was: {value}","messagePattern":"Component log level for '(.+?)' must be a string, was: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/logging/mod.rs","lineNumber":248,"sourceCode":"    }\n    LevelFilter::from_str(&log_level_str)\n        .map_err(|_| anyhow::anyhow!(\"Invalid log level string: '{s}'\"))\n}\n\n/// Parses component-specific log levels from a JSON value map.\n///\n/// # Errors\n///\n/// Returns an error if a JSON value in the map is not a string or is not a valid log level.\npub fn parse_component_levels(\n    original_map: Option<HashMap<String, serde_json::Value>>,\n) -> anyhow::Result<AHashMap<Ustr, LevelFilter>> {\n    let mut new_map = AHashMap::new();\n\n    for (key, value) in original_map.unwrap_or_default() {\n        let ustr_key = Ustr::from(&key);\n        let s = value.as_str().ok_or_else(|| {\n            anyhow::anyhow!(\"Component log level for '{key}' must be a string, was: {value}\")\n        })?;\n        let lvl = parse_level_filter_str(s)?;\n        new_map.insert(ustr_key, lvl);\n    }\n\n    Ok(new_map)\n}\n\n/// Logs that a task has started.\npub fn log_task_started(task_name: &str) {\n    log::debug!(\"Started task '{task_name}'\");\n}\n\n/// Logs that a task has stopped.\npub fn log_task_stopped(task_name: &str) {\n    log::debug!(\"Stopped task '{task_name}'\");\n}\n","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/logging/mod.rs#L230-L266","documentation":"Raised by parse_component_levels when an entry in the component log-level map has a value that is not a JSON string. Each component level must be a string like \"INFO\" or \"DEBUG\"; the error reports the offending key and the actual JSON value.","triggerScenarios":"Passing a JSON map to parse_component_levels where a component's level is a number, boolean, null, or nested object instead of a string, e.g. {\"DataEngine\": 3} or {\"RiskEngine\": true}.","commonSituations":"Malformed JSON log configuration files, programmatically built config objects with wrong types, YAML/JSON configs where levels were written unquoted.","solutions":["Make every component level value a JSON string, e.g. {\"DataEngine\": \"DEBUG\"}.","Quote unquoted levels in YAML-derived configs so they parse as strings, not booleans/numbers.","Validate the config schema before passing it to parse_component_levels."],"exampleFix":"// before\nparse_component_levels(Some(json!({\"DataEngine\": 3})))?;\n// after\nparse_component_levels(Some(json!({\"DataEngine\": \"DEBUG\"})))?;","handlingStrategy":"validation","validationCode":"fn validate_component_levels(map: &serde_json::Map<String, serde_json::Value>) -> Result<(), String> {\n    for (k, v) in map {\n        if v.as_str().is_none() {\n            return Err(format!(\"component '{k}' level must be a string, got: {v}\"));\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Quote level values in JSON/YAML configs so they always parse as strings.","Validate the config schema (all values are strings) before calling parse_component_levels.","Avoid building config maps programmatically with non-string level values.","Add a schema test covering a non-string level entry to catch config regressions."],"tags":["rust","logging","json","configuration","type-error"],"backgroundTag":"config-type-mismatch","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}