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

  1. Restore snapshot blobs in ascending snapshot_index order.
  2. Locate and restore the missing frame reported in the message (`frames.len()`) before continuing.
  3. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/1be6585ca53b8a0f. Report an issue: GitHub.