{"record":{"id":"1bd4cbb990cce4e8","repo":"nautechsystems/nautilus_trader","slug":"instrument-close-not-found-in-redis-key","errorCode":null,"errorMessage":"Instrument close not found in Redis: {key}","messagePattern":"Instrument close not found in Redis: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/infrastructure/src/redis/queries.rs","lineNumber":429,"sourceCode":"    /// Returns an error if scanning, reading, parsing, or deserializing instrument closes fails.\n    pub async fn load_instrument_closes(\n        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.","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/infrastructure/src/redis/queries.rs#L411-L447","documentation":"load_instrument_closes reads a bulk set of keys from Redis and zips keys with values. If Redis returns None (nil) for a key that the instrument-close index listed — the key existed in the index but the value is missing — this error is thrown naming the missing key.","triggerScenarios":"Calling load_instrument_closes / load_all when the Redis index of instrument-close keys contains a key whose value has expired (TTL), was deleted, or the bulk read (MGET) returned a nil slot.","commonSituations":"Values written with an expiry_ms TTL that later expired while the index entry persisted; manual deletion of value keys; Redis persistence restore that lost values but kept the index; replica with partial data.","solutions":["Check the named key in Redis (redis-cli GET) — if expired/deleted, re-write the instrument close.","Rebuild or clean the instrument-close index so it no longer lists missing keys.","Avoid TTLs on values while index entries have none, or set matching TTLs.","Re-snapshot the cache from a fresh session to regenerate consistent index + values."],"exampleFix":"// before: value keys with TTL, index without TTL -> nil slots on read\nredis.setex(value_key, 3600, payload); redis.sadd(index_key, value_key);\n// after: persist values (no TTL) while indexed, or purge index entries on expiry\nredis.set(value_key, payload);","handlingStrategy":"validation","validationCode":"// Rust: verify all indexed keys exist before loading closes\nasync fn all_keys_exist(con: &mut ConnectionManager, keys: &[String]) -> bool {\n    let values: Vec<Option<Vec<u8>>> = redis::cmd(\"MGET\").arg(keys)\n        .query_async(con).await.unwrap_or_default();\n    values.iter().all(|v| v.is_some())\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(\"not found in Redis\") => {\n        // purge stale index entry or re-write the missing value, then retry once\n        rebuild_index(&mut con, trader_key).await?;\n        load_instrument_closes(&mut con, trader_key, encoding).await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Do not apply short TTLs to value keys whose index entries are persistent.","Enable Redis persistence (AOF/RDB) so value keys survive restarts along with the index.","Periodically reconcile index entries against existing value keys."],"tags":["redis","missing-data","ttl","cache"],"backgroundTag":"entity-not-found","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"}