From b8f64a5f3ecd9d054e1137d2f5bef82695ca5c90 Mon Sep 17 00:00:00 2001 From: Bill Date: Thu, 20 Aug 2026 13:47:06 -0400 Subject: [PATCH] fix(store): drop undecodable workspace snapshots instead of bricking startup A snapshot persisted by an older binary (pre-#143, before WorkspaceSnapshot gained tree_sha) fails serde round-trip on load. restore_run_record propagated the error, so load_into aborted and the whole server refused to start. The store is best-effort: log and continue, matching the session-key and broker-message restore paths. --- crates/preloop-runner-server/src/store.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/preloop-runner-server/src/store.rs b/crates/preloop-runner-server/src/store.rs index 56c86110..540fa63c 100644 --- a/crates/preloop-runner-server/src/store.rs +++ b/crates/preloop-runner-server/src/store.rs @@ -555,9 +555,18 @@ pub(crate) fn restore_run_record(cipher: &Envelope, blob: &[u8]) -> anyhow::Resu .unwrap_or_default() .to_owned(); // `run_record_value` always writes the key (null when absent), so a - // JSON null must restore as `None` rather than fail to parse. + // JSON null must restore as `None` rather than fail to parse. A + // snapshot whose shape this binary no longer understands is dropped + // with a warning: the store is best-effort and one stale record must + // not brick startup (see `load_into`). run.workspace_snapshot = match object.get("workspace_snapshot") { - Some(value) if !value.is_null() => Some(serde_json::from_value(value.clone())?), + Some(value) if !value.is_null() => match serde_json::from_value(value.clone()) { + Ok(snapshot) => Some(snapshot), + Err(error) => { + tracing::warn!(run_id = %run.run_id, %error, "dropping undecodable workspace snapshot on load"); + None + } + }, _ => None, }; }