{"record":{"id":"0e306a8c385288b3","repo":"nautechsystems/nautilus_trader","slug":"canonical-result-summary-values-must-be-strings","errorCode":null,"errorMessage":"canonical result summary values must be strings","messagePattern":"canonical result summary values must be strings","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/result.rs","lineNumber":350,"sourceCode":"        \"orders\",\n        \"portfolio_snapshots\",\n        \"position_snapshots\",\n        \"positions\",\n    ] {\n        anyhow::ensure!(\n            object.get(key).is_some_and(Value::is_array),\n            \"canonical result field '{key}' must be an array\"\n        );\n    }\n    validate_components(object.get(\"components\").expect(\"validated field\"))?;\n    validate_diagnostics(object.get(\"diagnostics\").expect(\"validated field\"))?;\n    validate_run(object.get(\"run\").expect(\"validated field\"))?;\n    validate_statistics(object.get(\"statistics\").expect(\"validated field\"))?;\n    let summary = object\n        .get(\"summary\")\n        .and_then(Value::as_object)\n        .ok_or_else(|| anyhow::anyhow!(\"canonical result summary must be an object\"))?;\n    anyhow::ensure!(\n        summary.values().all(Value::is_string),\n        \"canonical result summary values must be strings\"\n    );\n    Ok(())\n}\n\nfn validate_fields(\n    object: &Map<String, Value>,\n    expected: &[&str],\n    context: &str,\n) -> anyhow::Result<()> {\n    let actual = object.keys().map(String::as_str).collect::<BTreeSet<_>>();\n    let expected = expected.iter().copied().collect::<BTreeSet<_>>();\n    anyhow::ensure!(\n        actual == expected,\n        \"{context} fields do not match the version 1 schema\"\n    );\n    Ok(())","sourceCodeStart":332,"sourceCodeEnd":368,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/result.rs#L332-L368","documentation":"During validation of a canonical backtest result document, the 'summary' entry must be a JSON object whose every value is a string. The library writes summaries as string-keyed/string-valued maps (produced via canonical_summary), so any non-string value (number, bool, null, array, nested object) means the document was hand-edited or produced by an incompatible writer. Thrown by validate_document, which runs both when a document is constructed (from_state) and when one is parsed (from_slice).","triggerScenarios":"Calling CanonicalResult::from_slice on JSON where summary contains a non-string value (e.g. \"summary\": {\"total_pnl\": 123} instead of \"123\"), or feeding a summary map with nested/null values into document construction.","commonSituations":"Hand-editing a result JSON file; generating the file with an older or external tool that writes numeric metrics unquoted; merging summaries from another schema version.","solutions":["Quote every value in the summary object so all values are JSON strings (convert numbers with .to_string() before writing)","Regenerate the document with the library's own canonicalization path instead of hand-editing","Check the schema field equals the current CANONICAL_SCHEMA version and re-run any migration tooling"],"exampleFix":"// before\n\"summary\": {\"total_pnl\": 1250.5}\n// after\n\"summary\": {\"total_pnl\": \"1250.5\"}","handlingStrategy":"validation","validationCode":"fn summary_values_are_strings(doc: &serde_json::Value) -> bool {\n    doc.get(\"summary\")\n        .and_then(serde_json::Value::as_object)\n        .map(|s| s.values().all(serde_json::Value::is_string))\n        .unwrap_or(false)\n}","typeGuard":"fn is_string_map(v: &serde_json::Value) -> bool {\n    v.as_object().map_or(false, |o| o.values().all(|x| x.is_string()))\n}","tryCatchPattern":"match CanonicalResult::from_slice(&bytes) {\n    Ok(result) => /* use result */,\n    Err(e) if e.to_string().contains(\"summary values must be strings\") => /* re-quote summary values or regenerate document */,\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Always serialize summary metrics with .to_string() so values are quoted","Never hand-edit canonical result JSON; regenerate it via the library","Validate documents against the v1 schema before feeding them to from_slice"],"tags":["json","schema-validation","backtest"],"backgroundTag":"schema-validation-failed","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"}