nautechsystems/nautilus_trader · error
malformed position snapshot blob_ref {blob_ref}
Error message
malformed position snapshot blob_ref {blob_ref} What it means
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.
Source
Thrown at crates/common/src/cache/position.rs:450
self.position_snapshots
.keys()
.filter(|position_id| {
self.positions
.get(position_id)
.is_some_and(|position| position.borrow().instrument_id == *instrument_id)
})
.copied()
.collect()
}
}
fn parse_position_snapshot_blob_ref(blob_ref: &str) -> anyhow::Result<(PositionId, usize)> {
let Some(rest) = blob_ref.strip_prefix("cache://position-snapshots/") else {
anyhow::bail!("unsupported cache snapshot blob_ref {blob_ref}");
};
let Some((position_id, snapshot_index)) = rest.rsplit_once('/') else {
anyhow::bail!("malformed position snapshot blob_ref {blob_ref}");
};
if position_id.is_empty() {
anyhow::bail!("position snapshot blob_ref {blob_ref} has empty position id");
}
let snapshot_index = snapshot_index.parse::<usize>().map_err(|e| {
anyhow::anyhow!("position snapshot blob_ref {blob_ref} has invalid frame index: {e}")
})?;
Ok((PositionId::new(position_id), snapshot_index))
}
fn decode_position_snapshot_blob(
position_id: &PositionId,
blob: &[u8],
) -> anyhow::Result<Position> {
let snapshot = serde_json::from_slice::<Position>(blob)?;View on GitHub (pinned to 18893faf8b)
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)
Example fix
// before
let blob_ref = format!("cache://position-snapshots/{position_id}");
// after
let blob_ref = format!("cache://position-snapshots/{position_id}/{snapshot_index}"); Defensive patterns
Strategy: validation
Validate before calling
fn blob_ref_has_index(blob_ref: &str) -> bool {
blob_ref.strip_prefix("cache://position-snapshots/")
.map(|rest| rest.contains('/'))
.unwrap_or(false)
} Type guard
fn parse_ref_parts(blob_ref: &str) -> Option<(&str, &str)> {
blob_ref.strip_prefix("cache://position-snapshots/")?.rsplit_once('/')
} Try / catch
if let Err(e) = cache.restore_snapshot_blob(blob_ref, blob) {
if e.to_string().contains("malformed position snapshot blob_ref") {
// rebuild blob_ref with '/<index>' suffix and retry once
}
} Prevention
- 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
When it happens
Trigger: Calling position_snapshot_frame or restore_snapshot_blob with a blob_ref like 'cache://position-snapshots/P-123' (no '/<index>' suffix).
Common situations: 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>'.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- unsupported cache snapshot blob_ref {blob_ref}
- position snapshot blob_ref {blob_ref} has empty position id
- DataActor {} must be registered before calling `cache()` - t
- Order {client_order_id} not found
- Position {position_id} not found
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/a5c7cf547e771d65.
Report an issue: GitHub.