{"record":{"id":"5c0f130a0dab8bdf","repo":"nautechsystems/nautilus_trader","slug":"instrument-close-key-id-instrument-id-did-not-ma","errorCode":null,"errorMessage":"Instrument close key ID {instrument_id} did not match payload ID {}","messagePattern":"Instrument close key ID (.+?) did not match payload ID (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/infrastructure/src/redis/queries.rs","lineNumber":431,"sourceCode":"        con: &ConnectionManager,\n        trader_key: &str,\n        encoding: SerializationEncoding,\n    ) -> anyhow::Result<AHashMap<InstrumentId, InstrumentClose>> {\n        let prefix = format!(\"{trader_key}{REDIS_DELIMITER}{INSTRUMENT_CLOSES}{REDIS_DELIMITER}\");\n        let pattern = format!(\"{prefix}*\");\n        log::debug!(\"Loading {pattern}\");\n\n        let mut con = con.clone();\n        let keys = Self::scan_keys(&mut con, pattern).await?;\n        let values = Self::read_bulk(&con, &keys).await?;\n        let mut closes = AHashMap::with_capacity(keys.len());\n\n        for (key, value) in keys.into_iter().zip(values) {\n            let instrument_id = parse_instrument_key(&key, &prefix)?;\n            let value = value\n                .ok_or_else(|| anyhow::anyhow!(\"Instrument close not found in Redis: {key}\"))?;\n            let close: InstrumentClose = Self::deserialize_payload(encoding, &value)?;\n            anyhow::ensure!(\n                close.instrument_id == instrument_id,\n                \"Instrument close key ID {instrument_id} did not match payload ID {}\",\n                close.instrument_id,\n            );\n            closes.insert(instrument_id, close);\n        }\n\n        log::debug!(\"Loaded {} instrument close(s)\", closes.len());\n        Ok(closes)\n    }\n\n    /// Loads all synthetic instruments for `trader_key` using the specified `encoding`.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if scanning keys or reading synthetic instrument data fails.\n    pub async fn load_synthetics(\n        con: &ConnectionManager,","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/infrastructure/src/redis/queries.rs#L413-L449","documentation":"After deserializing an InstrumentClose, load_instrument_closes verifies that the instrument_id embedded in the payload matches the instrument_id parsed from the Redis key. A mismatch means the value stored under one instrument's key actually belongs to a different instrument — an internal consistency violation.","triggerScenarios":"Calling load_instrument_closes / load_all when a value key was overwritten by a payload for a different instrument, keys/values were mismatched in the index, or an instrument_id was renamed (e.g. venue/symbol change) while old payloads persisted.","commonSituations":"Manually copying/renaming Redis keys; symbol migrations or instrument re-definitions between versions; concurrent writers clobbering each other's value keys; a bug in a custom snapshot tool.","solutions":["Inspect the named key and its payload's instrument_id; delete or move the misplaced payload.","Re-snapshot the cache so keys and payload IDs are regenerated consistently.","Check for concurrent writers using the same trader_id/database writing conflicting data.","If instrument IDs were renamed, run a migration script to rewrite payloads' instrument_id fields to match new keys."],"exampleFix":"// before: key says EURUSD.IDEAL but payload was written for EURUSD.SIM -> mismatch\n// after: rewrite payload under correct key / fix payload ID\ncon.set(\"closes:EURUSD.SIM\", serialize(InstrumentClose { instrument_id: \"EURUSD.SIM\", .. }));","handlingStrategy":"validation","validationCode":"// Rust: check key/payload ID agreement before insert\nfn ids_agree(key_id: &InstrumentId, close: &InstrumentClose) -> bool {\n    close.instrument_id == *key_id\n}","typeGuard":null,"tryCatchPattern":"match load_instrument_closes(&mut con, trader_key, encoding).await {\n    Ok(closes) => closes,\n    Err(e) if e.to_string().contains(\"did not match payload ID\") => {\n        tracing::error!(\"corrupt instrument-close entry: {e}; re-snapshot required\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Use a dedicated trader_id and Redis DB per instance to avoid concurrent writers clobbering keys.","Never rename/copy value keys manually; rewrite payloads through the library so embedded IDs stay in sync.","Re-snapshot the cache after instrument symbol/venue migrations."],"tags":["redis","data-integrity","consistency","cache"],"backgroundTag":"internal-invariant-violation","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"}