{"record":{"id":"2bf12878ef2c6d56","repo":"nautechsystems/nautilus_trader","slug":"invalid-instrument-key-key","errorCode":null,"errorMessage":"Invalid instrument key '{key}'","messagePattern":"Invalid instrument key '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/infrastructure/src/redis/queries.rs","lineNumber":1021,"sourceCode":"    }\n\n    async fn read_hset(conn: &mut ConnectionManager, key: &str) -> anyhow::Result<Vec<Bytes>> {\n        let result: HashMap<String, String> = conn.hgetall(key).await?;\n        let json = serde_json::to_string(&result)?;\n        Ok(vec![Bytes::from(json.into_bytes())])\n    }\n\n    async fn read_list(conn: &mut ConnectionManager, key: &str) -> anyhow::Result<Vec<Bytes>> {\n        let result: Vec<Bytes> = conn.lrange(key, 0, -1).await?;\n        Ok(result)\n    }\n}\n\nfn parse_instrument_key(key: &str, prefix: &str) -> anyhow::Result<InstrumentId> {\n    let value = key\n        .strip_prefix(prefix)\n        .filter(|value| !value.is_empty())\n        .ok_or_else(|| anyhow::anyhow!(\"Invalid instrument key '{key}'\"))?;\n    InstrumentId::from_str(value)\n        .map_err(|e| anyhow::anyhow!(\"Failed to parse instrument ID from key '{key}': {e}\"))\n}\n\nfn is_timestamp_field(key: &str) -> bool {\n    let expire_match = key == \"expire_time_ns\";\n    let ts_match = key.starts_with(\"ts_\");\n    expire_match || ts_match\n}\n\nfn convert_timestamps(value: &mut Value) {\n    match value {\n        Value::Object(map) => {\n            for (key, v) in map {\n                if is_timestamp_field(key)\n                    && let Value::Number(n) = v\n                    && let Some(n) = n.as_u64()\n                {","sourceCodeStart":1003,"sourceCodeEnd":1039,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/infrastructure/src/redis/queries.rs#L1003-L1039","documentation":"parse_instrument_key strips the configured prefix from a Redis instrument key and requires a non-empty remainder; otherwise it cannot recover the InstrumentId and throws 'Invalid instrument key'. This happens before InstrumentId parsing, so the key either lacks the prefix or is empty after stripping.","triggerScenarios":"load_instruments or load_instrument_closes (via load_all) iterating keys where one does not start with the expected prefix (e.g. prefix config mismatch, foreign keys matched by the scan pattern, or an empty key).","commonSituations":"The Redis key_prefix config differs from the one used when writing; a shared Redis database contains unrelated keys matching the scan pattern; keys written by another tool without the prefix; empty string keys.","solutions":["Set the Redis key_prefix config to exactly match the prefix used when the cache was written.","Use a dedicated Redis database (or separate DB index) so scan patterns do not match foreign keys.","Delete non-conforming keys or move them out of the scanned pattern.","Log/skip bad keys in custom tooling before handing them to the loader."],"exampleFix":"// before: stored under \"myapp:instruments:...\", reader uses default prefix\nlet config = RedisCacheConfig { key_prefix: \"nautilus\".into(), .. };\n// after\nlet config = RedisCacheConfig { key_prefix: \"myapp\".into(), .. };","handlingStrategy":"validation","validationCode":"// Rust: check key prefix before loading\nfn has_prefix(key: &str, prefix: &str) -> bool {\n    key.strip_prefix(prefix).map(|r| !r.is_empty()).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match load_instruments(&mut con, trader_key, encoding).await {\n    Ok(instruments) => instruments,\n    Err(e) if e.to_string().contains(\"Invalid instrument key\") => {\n        tracing::warn!(\"non-conforming keys found: {e}; check key_prefix config\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep RedisCacheConfig.key_prefix identical between snapshotting and loading sessions.","Use a dedicated Redis DB index to avoid scanning unrelated application keys.","Filter scanned keys by the configured prefix before feeding them to loaders."],"tags":["redis","key-format","instrument-id","config"],"backgroundTag":"invalid-argument-format","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}