{"record":{"id":"b5b8fd0e7e48bb13","repo":"nautechsystems/nautilus_trader","slug":"canonical-trader-state-must-be-a-string","errorCode":null,"errorMessage":"canonical trader state must be a string","messagePattern":"canonical trader state must be a string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/result.rs","lineNumber":396,"sourceCode":"            \"actor_ids\",\n            \"exec_algorithm_ids\",\n            \"strategy_ids\",\n            \"trader_state\",\n        ],\n        \"canonical result components\",\n    )?;\n\n    for key in [\"actor_ids\", \"exec_algorithm_ids\", \"strategy_ids\"] {\n        let values = object\n            .get(key)\n            .and_then(Value::as_array)\n            .ok_or_else(|| anyhow::anyhow!(\"canonical component field '{key}' must be an array\"))?;\n        anyhow::ensure!(\n            values.iter().all(Value::is_string),\n            \"canonical component field '{key}' must contain strings\"\n        );\n    }\n    anyhow::ensure!(\n        object.get(\"trader_state\").is_some_and(Value::is_string),\n        \"canonical trader state must be a string\"\n    );\n    Ok(())\n}\n\nfn validate_diagnostics(value: &Value) -> anyhow::Result<()> {\n    let diagnostics = value\n        .as_array()\n        .ok_or_else(|| anyhow::anyhow!(\"canonical diagnostics must be an array\"))?;\n    for diagnostic in diagnostics {\n        let object = diagnostic\n            .as_object()\n            .ok_or_else(|| anyhow::anyhow!(\"canonical diagnostic must be an object\"))?;\n        validate_fields(object, &[\"code\"], \"canonical diagnostic\")?;\n        anyhow::ensure!(\n            object.get(\"code\").and_then(Value::as_str) == Some(\"funding-settlement-failed\"),\n            \"unsupported canonical diagnostic code\"","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/result.rs#L378-L414","documentation":"The 'trader_state' field inside the canonical 'components' object must be a JSON string (it holds serialized trader state). validate_components throws this when the field is missing, null, or any non-string JSON value. It runs during both from_state construction and from_slice parsing.","triggerScenarios":"from_slice with \"components\": {..., \"trader_state\": null} or a nested object; constructing a document where trader state failed to serialize to a string.","commonSituations":"Omitting trader_state when assembling components by hand; a serializer emitting an object instead of its string-encoded form.","solutions":["Set trader_state to its string-encoded form (e.g. serialized state as a JSON string)","If state is genuinely empty, use an empty string \"\" rather than null","Fix the code path that serializes trader state so it always yields a string"],"exampleFix":"// before\n\"trader_state\": null\n// after\n\"trader_state\": \"{\\\"initialized\\\": true}\"","handlingStrategy":"validation","validationCode":"fn trader_state_is_string(c: &serde_json::Value) -> bool {\n    c.get(\"trader_state\").map_or(false, |v| v.is_string())\n}","typeGuard":"fn is_non_null_string(v: &serde_json::Value) -> bool { v.is_string() }","tryCatchPattern":"match CanonicalResult::from_slice(&bytes) {\n    Ok(result) => /* use result */,\n    Err(e) if e.to_string().contains(\"trader state must be a string\") => /* set trader_state to its string-encoded form */,\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Serialize trader state to its string form before placing it in components","Use an empty string, never null, when there is no state","Include trader_state whenever assembling components manually"],"tags":["json","type-mismatch","null"],"backgroundTag":"type-mismatch","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}