{"record":{"id":"a5c7cf547e771d65","repo":"nautechsystems/nautilus_trader","slug":"malformed-position-snapshot-blob-ref-blob-ref","errorCode":null,"errorMessage":"malformed position snapshot blob_ref {blob_ref}","messagePattern":"malformed position snapshot blob_ref (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/cache/position.rs","lineNumber":450,"sourceCode":"        self.position_snapshots\n            .keys()\n            .filter(|position_id| {\n                self.positions\n                    .get(position_id)\n                    .is_some_and(|position| position.borrow().instrument_id == *instrument_id)\n            })\n            .copied()\n            .collect()\n    }\n}\n\nfn parse_position_snapshot_blob_ref(blob_ref: &str) -> anyhow::Result<(PositionId, usize)> {\n    let Some(rest) = blob_ref.strip_prefix(\"cache://position-snapshots/\") else {\n        anyhow::bail!(\"unsupported cache snapshot blob_ref {blob_ref}\");\n    };\n\n    let Some((position_id, snapshot_index)) = rest.rsplit_once('/') else {\n        anyhow::bail!(\"malformed position snapshot blob_ref {blob_ref}\");\n    };\n\n    if position_id.is_empty() {\n        anyhow::bail!(\"position snapshot blob_ref {blob_ref} has empty position id\");\n    }\n\n    let snapshot_index = snapshot_index.parse::<usize>().map_err(|e| {\n        anyhow::anyhow!(\"position snapshot blob_ref {blob_ref} has invalid frame index: {e}\")\n    })?;\n\n    Ok((PositionId::new(position_id), snapshot_index))\n}\n\nfn decode_position_snapshot_blob(\n    position_id: &PositionId,\n    blob: &[u8],\n) -> anyhow::Result<Position> {\n    let snapshot = serde_json::from_slice::<Position>(blob)?;","sourceCodeStart":432,"sourceCodeEnd":468,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/cache/position.rs#L432-L468","documentation":"After stripping the 'cache://position-snapshots/' prefix, the remainder must contain a '/' separating the position id from the frame index (rsplit_once('/')). This error means the remainder has no separator, so the blob_ref lacks a frame index component.","triggerScenarios":"Calling position_snapshot_frame or restore_snapshot_blob with a blob_ref like 'cache://position-snapshots/P-123' (no '/<index>' suffix).","commonSituations":"Building the ref with only the position id; a writer/consumer version mismatch where older refs had no index; string slicing that dropped the trailing '/<index>'.","solutions":["Append the frame index: 'cache://position-snapshots/<position_id>/<frame_index>'","Regenerate the blob_ref via the cache's snapshot API instead of manual string construction","Validate the ref shape with a quick check before calling (contains a '/' after the prefix)"],"exampleFix":"// before\nlet blob_ref = format!(\"cache://position-snapshots/{position_id}\");\n// after\nlet blob_ref = format!(\"cache://position-snapshots/{position_id}/{snapshot_index}\");","handlingStrategy":"validation","validationCode":"fn blob_ref_has_index(blob_ref: &str) -> bool {\n    blob_ref.strip_prefix(\"cache://position-snapshots/\")\n        .map(|rest| rest.contains('/'))\n        .unwrap_or(false)\n}","typeGuard":"fn parse_ref_parts(blob_ref: &str) -> Option<(&str, &str)> {\n    blob_ref.strip_prefix(\"cache://position-snapshots/\")?.rsplit_once('/')\n}","tryCatchPattern":"if let Err(e) = cache.restore_snapshot_blob(blob_ref, blob) {\n    if e.to_string().contains(\"malformed position snapshot blob_ref\") {\n        // rebuild blob_ref with '/<index>' suffix and retry once\n    }\n}","preventionTips":["Format refs as format!(\"cache://position-snapshots/{position_id}/{index}\") so the index is never omitted","Round-trip test: write a snapshot, read it back via its ref in unit tests"],"tags":["cache","blob-ref","invalid-argument-format","rust"],"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"}