nautechsystems/nautilus_trader · error
position snapshot blob_ref {blob_ref} skips missing frame {}
Error message
position snapshot blob_ref {blob_ref} skips missing frame {} What it means
Snapshot frames must be restored contiguously: `restore_snapshot_blob` only appends a frame when `snapshot_index` equals the current length. A blob_ref whose index is greater than the number of stored frames would leave a gap, so the method bails reporting the first missing frame index.
Source
Thrown at crates/common/src/cache/position.rs:284
/// snapshot frames, conflicts with an existing frame, or does not decode to the expected
/// position snapshot.
pub fn restore_snapshot_blob(&mut self, blob_ref: &str, blob: Bytes) -> anyhow::Result<()> {
let (position_id, snapshot_index) = parse_position_snapshot_blob_ref(blob_ref)?;
let restored = decode_position_snapshot_blob(&position_id, blob.as_ref())?;
let frames = self.position_snapshots.entry(position_id).or_default();
match frames.get(snapshot_index) {
Some(existing) if existing.encoded()? == blob => {}
Some(_) => {
anyhow::bail!(
"position snapshot frame {snapshot_index} for {position_id} already exists with different bytes"
);
}
None if frames.len() == snapshot_index => {
frames.push(PositionSnapshotFrame::new(restored, Some(blob.clone())));
}
None => {
anyhow::bail!(
"position snapshot blob_ref {blob_ref} skips missing frame {}",
frames.len()
);
}
}
self.general.insert(blob_ref.to_string(), blob);
Ok(())
}
fn snapshot_blob(&self, blob_ref: &str) -> Option<Bytes> {
if let Some(blob) = self.general.get(blob_ref) {
return Some(blob.clone());
}
self.position_snapshot_frame(blob_ref)?
.encoded()
.inspect_err(|e| log::warn!("Failed to encode position snapshot {blob_ref}: {e}"))View on GitHub (pinned to 18893faf8b)
Solutions
- Restore snapshot blobs in ascending snapshot_index order.
- Locate and restore the missing frame reported in the message (`frames.len()`) before continuing.
- If the frame is unrecoverable, restore the position from a complete earlier snapshot lineage instead of a partial one.
Example fix
// before
for blob in blobs.iter().rev() { cache.restore_snapshot_blob(...)?; } // out of order
// after
let mut sorted = blobs.clone();
sorted.sort_by_key(|(blob_ref, _)| blob_ref.snapshot_index);
for (blob_ref, blob) in sorted { cache.restore_snapshot_blob(blob_ref, blob)?; } Defensive patterns
Strategy: validation
Validate before calling
// ensure frames are contiguous and sorted by snapshot_index before restoring sorted_by_index.iter().enumerate().all(|(i, (blob_ref, _))| blob_ref.snapshot_index == i);
Try / catch
match cache.restore_snapshot_blob(position_id, blob_ref, blob) {
Ok(()) => {},
Err(e) if e.to_string().contains("skips missing frame") => {
// restore the missing frame reported in the message, in order
},
Err(e) => return Err(e),
} Prevention
- Always restore snapshot blobs in ascending index order.
- Verify the full snapshot file set exists before starting a restore.
- Never skip corrupt frames mid-sequence; fall back to a complete snapshot.
When it happens
Trigger: Restoring blobs out of order (index N before N-1), or restoring with a skipped/deleted frame (e.g. a missing file in the snapshot sequence, or filtering frames during restore).
Common situations: Manual recovery from a snapshot directory where one frame file is missing or corrupt and was skipped; parallel restores racing frames into the cache out of order.
Related errors
- position snapshot frame {snapshot_index} for {position_id} a
- List of Positions is empty
- Unknown position currency '{pos_ccy}' for instrument {instru
- position snapshot id {} does not match blob_ref position {po
- No persisted order events found for {client_order_id}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/1be6585ca53b8a0f.
Report an issue: GitHub.