{"record":{"id":"abb096eaa23d5bbe","repo":"nautechsystems/nautilus_trader","slug":"canonical-component-field-key-must-be-an-array","errorCode":null,"errorMessage":"canonical component field '{key}' must be an array","messagePattern":"canonical component field '(.+?)' must be an array","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/result.rs","lineNumber":390,"sourceCode":"    let object = value\n        .as_object()\n        .ok_or_else(|| anyhow::anyhow!(\"canonical result components must be an object\"))?;\n    validate_fields(\n        object,\n        &[\n            \"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","sourceCodeStart":372,"sourceCodeEnd":408,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/result.rs#L372-L408","documentation":"Within the canonical 'components' object, the fields actor_ids, exec_algorithm_ids, and strategy_ids must each be a JSON array of strings. This error fires when one of these fields exists but is not an array (null, string, object, or a scalar). validate_components enforces it during document construction and parsing.","triggerScenarios":"from_slice with e.g. \"components\": {\"strategy_ids\": \"S-1\", ...} or \"actor_ids\": null; constructing a document where a component ID list was serialized as a single string or map.","commonSituations":"Collapsing a single-element list to a scalar when generating JSON; another writer emitting ID maps (object keyed by ID) instead of arrays.","solutions":["Wrap the value in an array: use [\"S-1\"] not \"S-1\"","Ensure null is replaced by an empty array [] for these three fields","Fix the producer code so ID collections are always serialized as JSON arrays of strings"],"exampleFix":"// before\n\"strategy_ids\": \"S-1\"\n// after\n\"strategy_ids\": [\"S-1\"]","handlingStrategy":"validation","validationCode":"fn component_id_fields_are_arrays(c: &serde_json::Value) -> bool {\n    [\"actor_ids\", \"exec_algorithm_ids\", \"strategy_ids\"].iter().all(|k| {\n        c.get(k).map_or(false, |v| v.is_array())\n    })\n}","typeGuard":"fn is_string_array(v: &serde_json::Value) -> bool {\n    v.as_array().map_or(false, |a| a.iter().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(\"must be an array\") => /* wrap the offending field in an array */,\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Never collapse a single-element ID list into a scalar","Use [] instead of null for empty ID collections","Serialize ID collections as Vec<String> in the producer"],"tags":["json","type-mismatch","arrays"],"backgroundTag":"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"}