{"record":{"id":"05cdb31a5e784610","repo":"nautechsystems/nautilus_trader","slug":"expected-json-array-for-contracts","errorCode":null,"errorMessage":"Expected JSON array for contracts","messagePattern":"Expected JSON array for contracts","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/interactive_brokers/src/common/contracts.rs","lineNumber":168,"sourceCode":"        combo_legs_description: get_str(\"comboLegsDescrip\"),\n        combo_legs: Vec::new(),       // TODO: Parse combo_legs if needed\n        delta_neutral_contract: None, // TODO: Parse delta_neutral_contract if needed\n        issuer_id: get_str(\"issuerId\"),\n        description: get_str(\"description\"),\n    })\n}\n\n/// Parse multiple IB contracts from JSON array.\n///\n/// # Errors\n///\n/// Returns an error if the JSON string is invalid or if any contract fails to parse.\npub fn parse_contracts_from_json_array(json_str: &str) -> anyhow::Result<Vec<Contract>> {\n    let value: Value = serde_json::from_str(json_str).context(\"Failed to parse JSON string\")?;\n\n    let array = value\n        .as_array()\n        .ok_or_else(|| anyhow::anyhow!(\"Expected JSON array for contracts\"))?;\n\n    let mut contracts = Vec::new();\n\n    for (idx, item) in array.iter().enumerate() {\n        match parse_contract_from_json(item) {\n            Ok(contract) => contracts.push(contract),\n            Err(e) => {\n                tracing::warn!(\"Failed to parse contract at index {}: {}\", idx, e);\n            }\n        }\n    }\n\n    Ok(contracts)\n}\n\nuse anyhow::Context;\n","sourceCodeStart":150,"sourceCodeEnd":185,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/interactive_brokers/src/common/contracts.rs#L150-L185","documentation":"`parse_contracts_from_json_array` parses a JSON string that must contain a top-level JSON array of contract objects. This error is thrown when the parsed value is a valid JSON but not an array (e.g. an object or scalar), so the subsequent per-element contract parsing cannot run.","triggerScenarios":"Passing a JSON string whose root is an object (e.g. `{\"contracts\": [...]}`) or a bare value (e.g. `\"[]\"` is fine but `[]` wrapped as a string is not) to `parse_contracts_from_json_array`.","commonSituations":"Cache files written by a different tool storing contracts under an object wrapper; the Python side sending a single contract object instead of a list; version mismatch between producer and consumer of the serialized contract dump.","solutions":["Ensure the JSON string's top level is an array: `[ {...}, {...} ]`.","If the payload is wrapped in an object, extract the array field first before calling the parser.","Verify the producer of the JSON (cache writer or Python bridge) serializes a `Vec<Contract>` / list, not a single contract.","Regenerate or fix the corrupted cache file."],"exampleFix":"// before\nlet contracts = parse_contracts_from_json_array(json_str)?;\n// after\nlet root: serde_json::Value = serde_json::from_str(json_str)?;\nlet arr = if let Some(a) = root.as_array() {\n    a.clone()\n} else {\n    vec![root] // treat single object as one-element list\n};\nlet contracts = parse_contracts_from_json_array(&serde_json::to_string(&arr)?)?;","handlingStrategy":"type-guard","validationCode":"let root: serde_json::Value = serde_json::from_str(json_str)?;\nif !root.is_array() {\n    return Err(anyhow::anyhow!(\"contracts payload must be a JSON array\"));\n}\n","typeGuard":"fn is_contract_array(v: &serde_json::Value) -> bool {\n    v.as_array().map_or(false, |a| a.iter().all(|e| e.is_object()))\n}\n","tryCatchPattern":"match parse_contracts_from_json_array(&json_str) {\n    Ok(contracts) => use(contracts),\n    Err(e) if e.to_string().contains(\"Expected JSON array\") => {\n        // fall back: try parsing as single contract object\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always serialize a Vec/list of contracts, never a single contract, into shared JSON payloads.","Round-trip test the cache writer/reader pair.","Check top-level JSON shape with jq or serde before handing off between components."],"tags":["json","interactive-brokers","contracts","type-mismatch"],"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"}