{"record":{"id":"3bf7de5c0adb3e77","repo":"atuinsh/atuin","slug":"stored-value-is-a-valid-commandcapture","errorCode":null,"errorMessage":"stored value is a valid CommandCapture","messagePattern":"stored value is a valid CommandCapture","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin-daemon/src/output_capture/backend/fjall/mod.rs","lineNumber":91,"sourceCode":"                    Ok(())\n                }\n                // Another writer committed this key first, so it's already captured.\n                Err(fjall::Conflict) => Err(CaptureError::AlreadyExists),\n            }\n        })\n        .await\n        .expect(\"output-capture write task panicked\")\n    }\n\n    async fn get(&self, id: HistoryId) -> Result<Option<CommandCapture>, GetOutputError> {\n        let keyspace = self.keyspace.clone();\n        let key = ActiveSchema::serialize_key(id).expect(\"history id serialization is infallible\");\n\n        tokio::task::spawn_blocking(move || {\n            match keyspace.get(key).map_err(|err| GetOutputError::Storage(Box::new(err)))? {\n                Some(slice) => {\n                    let capture = ActiveSchema::deserialize_value(slice.to_vec())\n                        .expect(\"stored value is a valid CommandCapture\");\n                    Ok(Some(capture))\n                }\n                None => Ok(None),\n            }\n        })\n        .await\n        .expect(\"output-capture read task panicked\")\n    }\n\n    async fn remove(&self, ids: Vec<HistoryId>) -> Result<(), DeleteOutputError> {\n        let keys: Vec<_> = ids\n            .into_iter()\n            .map(|id| {\n                ActiveSchema::serialize_key(id).expect(\"history id serialization is infallible\")\n            })\n            .collect();\n        if keys.is_empty() {\n            return Ok(());","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/atuinsh/atuin/blob/c0c717ab04c881764bcad4b3d169a507e2432643/crates/atuin-daemon/src/output_capture/backend/fjall/mod.rs#L73-L109","documentation":"`get()` in the fjall output-capture backend reads a stored value and calls `ActiveSchema::deserialize_value`, expecting all stored values to be valid `CommandCapture` blobs written by the same schema. Panicking means a value in the keyspace cannot be deserialized — corrupted data or a schema/version mismatch between writer and reader.","triggerScenarios":"Calling `get(id)` for a history id whose stored bytes were written by a different Atuin/fjall schema version, were corrupted on disk, or were truncated by an unclean shutdown.","commonSituations":"Downgrading Atuin after a schema change; restoring a partial/inconsistent data directory from backup; disk corruption; manually copying keyspace files between machines with different versions.","solutions":["Ensure the Atuin version reading the data is not older than the version that wrote it","If corruption is suspected, delete/rebuild the output-capture keyspace from history","Restore from a consistent backup that includes matching schema versions","Report a bug if it reproduces on a single unmodified installation"],"exampleFix":"// before\n.expect(\"stored value is a valid CommandCapture\");\n// after\nActiveSchema::deserialize_value(slice.to_vec())\n    .map_err(|err| GetOutputError::Schema(Box::new(err)))?","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Treat undecodable values as missing instead of panicking:\nlet capture = match ActiveSchema::deserialize_value(slice.to_vec()) {\n    Ok(c) => c,\n    Err(e) => { tracing::warn!(\"undecodable capture: {e}\"); return Ok(None); }\n};","preventionTips":["Don't downgrade Atuin across schema versions on the same data directory","Back up and restore the whole data directory consistently","Check filesystem health (fsck, SMART) if corruption recurs","Treat stored blobs as untrusted input and decode fallibly"],"tags":["storage","deserialization","schema-mismatch","corruption"],"backgroundTag":"schema-validation-failed","analyzedSha":"c0c717ab04c881764bcad4b3d169a507e2432643","analyzedAt":"2026-09-12T07:40:01.341Z","contentChangedAt":"2026-09-12T07:40:01.341Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}