{"record":{"id":"2c5f61a967df21b1","repo":"nautechsystems/nautilus_trader","slug":"expected-json-object-for-contract","errorCode":null,"errorMessage":"Expected JSON object for contract","messagePattern":"Expected JSON object for contract","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/interactive_brokers/src/common/contracts.rs","lineNumber":85,"sourceCode":"fn security_type_to_code(security_type: &SecurityType) -> String {\n    IbSecurityType::try_from(security_type).map_or_else(\n        |_| security_type.to_string(),\n        |security_type| security_type.to_string(),\n    )\n}\n\n/// Parse IB contract from JSON dictionary.\n///\n/// This function parses a JSON object (dictionary) representing an IBContract\n/// and converts it to a rust-ibapi Contract struct.\n///\n/// # Errors\n///\n/// Returns an error if the JSON is not a valid object or if required fields are missing.\npub fn parse_contract_from_json(json: &Value) -> anyhow::Result<Contract> {\n    let obj = json\n        .as_object()\n        .ok_or_else(|| anyhow::anyhow!(\"Expected JSON object for contract\"))?;\n\n    let get_str = |key: &str| -> String {\n        obj.get(key)\n            .and_then(|v| v.as_str())\n            .unwrap_or_default()\n            .to_string()\n    };\n\n    let get_i32 = |key: &str| -> i32 {\n        obj.get(key)\n            .and_then(|v| v.as_i64())\n            .map_or(0, |n| n as i32)\n    };\n\n    let get_f64 = |key: &str| -> f64 { obj.get(key).and_then(|v| v.as_f64()).unwrap_or(0.0) };\n\n    let get_bool = |key: &str| -> bool { obj.get(key).and_then(|v| v.as_bool()).unwrap_or(false) };\n","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/interactive_brokers/src/common/contracts.rs#L67-L103","documentation":"`parse_contract_from_json` expects a `serde_json::Value` that is a JSON object with contract fields (symbol, exchange, sec_type, etc.). This error is thrown when the passed value is not an object (e.g. a string, number, array, or null), so contract deserialization cannot proceed.","triggerScenarios":"Calling `parse_contract_from_json` directly with a non-object `Value`, or via `parse_contracts_from_json_array`, `load_contract`, `contract_from_instrument_info`, `load_cache`, or `py_to_contract` when the underlying JSON element is a scalar/string/array rather than an object.","commonSituations":"A cache file or IPC/Python boundary produced contract entries as JSON strings (double-encoded) instead of objects; a contracts array contains `null` or numeric placeholders; hand-edited cache JSON is malformed.","solutions":["Inspect the JSON value before parsing; ensure each contract entry is a JSON object like `{\"symbol\": \"AAPL\", ...}`.","Fix double-encoded JSON: if the value is a string containing JSON, call `serde_json::from_str::<Value>(s)` first to unwrap it.","Filter out null/non-object entries before iterating an array of contracts.","Regenerate the contract cache file if it was hand-edited or written by an older adapter version."],"exampleFix":"// before\nlet contract = parse_contract_from_json(&value)?;\n// after\nif !value.is_object() {\n    return Err(anyhow::anyhow!(\"skipping non-object contract entry\"));\n}\nlet contract = parse_contract_from_json(&value)?;","handlingStrategy":"type-guard","validationCode":"if !json.is_object() {\n    return Err(anyhow::anyhow!(\"contract entry must be a JSON object\"));\n}\n","typeGuard":"fn is_contract_object(v: &serde_json::Value) -> bool {\n    v.is_object() && v.get(\"symbol\").map_or(false, |s| s.is_string())\n}\n","tryCatchPattern":"match parse_contract_from_json(&value) {\n    Ok(c) => contracts.push(c),\n    Err(e) if e.to_string().contains(\"Expected JSON object\") => {\n        log::warn!(\"skipping non-object contract entry: {value}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Never double-encode contracts as JSON strings inside JSON.","Validate cache files with a schema check on load.","Filter nulls and scalars out of contract arrays before parsing."],"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"}