{"record":{"id":"26f33b2b060a0996","repo":"nautechsystems/nautilus_trader","slug":"canonical-statistics-field-key-must-be-an-obje","errorCode":null,"errorMessage":"canonical statistics field '{key}' must be an object","messagePattern":"canonical statistics field '(.+?)' must be an object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/result.rs","lineNumber":500,"sourceCode":"                && !value.starts_with('0')\n                && value.bytes().all(|byte| byte.is_ascii_digit())),\n        \"canonical run field '{field}' is not a canonical unsigned decimal\"\n    );\n    Ok(())\n}\n\nfn validate_statistics(value: &Value) -> anyhow::Result<()> {\n    let object = value\n        .as_object()\n        .ok_or_else(|| anyhow::anyhow!(\"canonical result statistics must be an object\"))?;\n    validate_fields(\n        object,\n        &[\"general\", \"pnls\", \"returns\", \"returns_series\"],\n        \"canonical result statistics\",\n    )?;\n\n    for key in [\"general\", \"pnls\", \"returns\"] {\n        anyhow::ensure!(\n            object.get(key).is_some_and(Value::is_object),\n            \"canonical statistics field '{key}' must be an object\"\n        );\n    }\n    anyhow::ensure!(\n        object.get(\"returns_series\").is_some_and(Value::is_array),\n        \"canonical returns series must be an array\"\n    );\n    Ok(())\n}\n\nfn optional_nanos(value: Option<UnixNanos>) -> Value {\n    value.map_or(Value::Null, |nanos| Value::String(nanos.to_string()))\n}\n\nfn canonical_order(order: &OrderAny) -> anyhow::Result<Value> {\n    let mut value = serde_json::to_value(order)?;\n    let payload = variant_payload_mut(&mut value)?;","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/result.rs#L482-L518","documentation":"Within the canonical statistics object, the fields `general`, `pnls`, and `returns` must each be JSON objects (maps of metric name to value). This error is thrown when any of these keys holds a different type — array, string, number, or null. It ensures consumers can iterate metric key/value pairs uniformly.","triggerScenarios":"Validating a document where statistics.general, statistics.pnls, or statistics.returns is not an object — e.g. an array of [name, value] pairs, a flattened string, or null for an empty section. Common when statistics were serialized from a different in-memory representation.","commonSituations":"Exporting metrics as arrays of tuples from Python dicts with non-string keys; nulling out empty stat sections; merging statistics from multiple sources into the wrong shape; older fixture formats.","solutions":["Make general, pnls, and returns plain JSON objects mapping metric names to values","Use null-safe defaults: emit {} for empty sections rather than null or arrays","Regenerate the document via the library's canonical writer","Transform array/tuple-based stat representations into objects before validation"],"exampleFix":"// before\n{\"statistics\": {\"pnls\": [[\"PnL\", \"100\"]], \"returns\": null}}\n// after\n{\"statistics\": {\"pnls\": {\"PnL\": \"100\"}, \"returns\": {}}}","handlingStrategy":"validation","validationCode":"fn stat_sections_are_objects(stats: &serde_json::Value) -> bool {\n    [\"general\", \"pnls\", \"returns\"]\n        .iter()\n        .all(|k| stats.get(*k).map_or(false, serde_json::Value::is_object))\n}","typeGuard":"fn is_metric_map(v: &serde_json::Value) -> bool {\n    v.is_object() && v.as_object().map_or(false, |m| m.iter().all(|(k, _)| !k.is_empty()))\n}","tryCatchPattern":"match validate_document(&doc) {\n    Err(e) if e.to_string().contains(\"must be an object\") => {\n        // convert offending statistics section to an object and retry\n    }\n    other => other,\n}","preventionTips":["Serialize metric maps as JSON objects keyed by metric name","Emit {} for empty sections instead of null or arrays","Convert tuple/pair lists to maps before canonicalization","Cover statistics serialization with round-trip tests"],"tags":["json","validation","schema","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-14T05:17:10.506Z"}