{"record":{"id":"41b1b2a05891b539","repo":"nautechsystems/nautilus_trader","slug":"canonical-returns-series-must-be-an-array","errorCode":null,"errorMessage":"canonical returns series must be an array","messagePattern":"canonical returns series must be an array","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/result.rs","lineNumber":505,"sourceCode":"}\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)?;\n    let core = payload\n        .get_mut(\"core\")\n        .and_then(Value::as_object_mut)\n        .ok_or_else(|| anyhow::anyhow!(\"serialized order did not contain an object core\"))?;\n    set_decimal(core, \"avg_px\", order.avg_px());","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/result.rs#L487-L523","documentation":"The `returns_series` field of the canonical statistics object must be a JSON array (an ordered series of return samples). This error is thrown when it is any other JSON type — object, string, number, or null. Unlike the other statistics fields which are objects, returns_series is inherently ordered, hence the array requirement.","triggerScenarios":"Validating a document where statistics.returns_series is an object (e.g. a map of timestamp->value), null, or a scalar instead of an array. Occurs when series data was serialized as a dict keyed by timestamp or when empty series were encoded as null.","commonSituations":"Series built from Python/JS dicts (keyed by timestamp) then serialized as objects; empty series encoded as null; schema drift from an older format that used an object map for the series.","solutions":["Serialize returns_series as a JSON array of entries preserving order","Convert timestamp-keyed maps to arrays before validation","Emit [] for an empty series instead of null","Regenerate the canonical document with the current library version"],"exampleFix":"// before\n{\"statistics\": {\"returns_series\": {\"1720000000000000000\": \"0.01\"}}}\n// after\n{\"statistics\": {\"returns_series\": [{\"ts_init\": \"1720000000000000000\", \"value\": \"0.01\"}]}}","handlingStrategy":"validation","validationCode":"fn returns_series_is_array(stats: &serde_json::Value) -> bool {\n    stats.get(\"returns_series\").map_or(false, serde_json::Value::is_array)\n}","typeGuard":"fn is_array(v: &serde_json::Value) -> bool { v.is_array() }","tryCatchPattern":"match validate_document(&doc) {\n    Err(e) if e.to_string().contains(\"returns series must be an array\") => {\n        // convert map/none series to an array and retry\n    }\n    other => other,\n}","preventionTips":["Store return samples in ordered collections (Vec/array), not keyed maps","Serialize empty series as [] not null","Preserve sample order through any transformations","Add a validator run to the result export pipeline"],"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-14T00:17:10.932Z"}