{"record":{"id":"a9cea443d374b151","repo":"nautechsystems/nautilus_trader","slug":"canonical-result-components-must-be-an-object","errorCode":null,"errorMessage":"canonical result components must be an object","messagePattern":"canonical result components must be an object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/result.rs","lineNumber":374,"sourceCode":"\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(())\n}\n\nfn validate_components(value: &Value) -> anyhow::Result<()> {\n    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),","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/result.rs#L356-L392","documentation":"The 'components' section of a canonical result must be a JSON object with keys actor_ids, exec_algorithm_ids, strategy_ids, and trader_state. This error is thrown when the value is null, an array, a string, or any non-object JSON value. It runs during validate_document for both parsed and constructed documents.","triggerScenarios":"from_slice on JSON with \"components\": [] or \"components\": null, or constructing a document whose components entry is not a JSON object.","commonSituations":"Hand-editing or programmatically assembling result JSON and serializing components as a list or omitting it to null; a tool from another schema emitting components as an array.","solutions":["Change components to an object: {\"actor_ids\": [...], \"exec_algorithm_ids\": [...], \"strategy_ids\": [...], \"trader_state\": \"...\"}","Check that the serialization code that builds 'components' emits a map, not a list","Regenerate the document with the library's canonical writer"],"exampleFix":"// before\n\"components\": []\n// after\n\"components\": {\"actor_ids\": [], \"exec_algorithm_ids\": [], \"strategy_ids\": [\"S-1\"], \"trader_state\": \"...\"}","handlingStrategy":"validation","validationCode":"fn components_is_object(doc: &serde_json::Value) -> bool {\n    doc.get(\"components\").map_or(false, |v| v.is_object())\n}","typeGuard":"fn is_object(v: &serde_json::Value) -> bool { v.is_object() }","tryCatchPattern":"match CanonicalResult::from_slice(&bytes) {\n    Ok(result) => /* use result */,\n    Err(e) if e.to_string().contains(\"components must be an object\") => /* convert components to an object and retry */,\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Emit components as a map with the four required keys, never as a list","Use [] arrays for the ID fields and a string for trader_state","Regenerate documents with the library instead of assembling JSON by hand"],"tags":["json","type-mismatch","schema-validation"],"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"}