{"record":{"id":"036916a708308af8","repo":"atuinsh/atuin","slug":"output-capture-write-task-panicked","errorCode":null,"errorMessage":"output-capture write task panicked","messagePattern":"output-capture write task panicked","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin-daemon/src/output_capture/backend/fjall/mod.rs","lineNumber":80,"sourceCode":"            if tx\n                .contains_key(&keyspace, key)\n                .map_err(|err| CaptureError::Storage(Box::new(err)))?\n            {\n                return Err(CaptureError::AlreadyExists);\n            }\n\n            tx.insert(&keyspace, key, value);\n            match tx.commit().map_err(|err| CaptureError::Storage(Box::new(err)))? {\n                Ok(()) => {\n                    dirty.store(true, Ordering::Release);\n                    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\")","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/atuinsh/atuin/blob/c0c717ab04c881764bcad4b3d169a507e2432643/crates/atuin-daemon/src/output_capture/backend/fjall/mod.rs#L62-L98","documentation":"The fjall-backed output capture runs its write path inside `tokio::task::spawn_blocking` and `.expect()`s the JoinHandle. Panicking means the spawned blocking task itself panicked (not a storage error — those are mapped to `CaptureError`). The storage engine thread crashed mid-write.","triggerScenarios":"Any `capture()` call where the inner closure panics — e.g. a fjall internal panic on corrupted log segments, allocation failure, or panic in serialization inside the blocking task.","commonSituations":"Disk-full or I/O errors surfacing as panics inside fjall; corrupted fjall data directory after an unclean shutdown; OOM killer killing threads mid-write; fjall version bugs.","solutions":["Check disk space and filesystem health on the directory holding the fjall keyspace","Inspect/repair or delete the corrupted fjall data directory (it can be rebuilt from history)","Look for the underlying panic message in logs — the expect here masks it unless captured","Update fjall to the latest patched version","Report the inner panic to Atuin if data is intact and reproducible"],"exampleFix":"// before\n.await.expect(\"output-capture write task panicked\")\n// after\n.await.map_err(|e| CaptureError::Storage(Box::new(e)))?","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Handle JoinError from spawn_blocking:\nmatch handle.await {\n    Ok(res) => res,\n    Err(join_err) => return Err(CaptureError::Storage(Box::new(join_err))),\n}","preventionTips":["Monitor disk space on the fjall data directory","Always shut the daemon down cleanly to avoid log-segment corruption","Keep fjall updated to patched releases","Log inner panics (catch_unwind or JoinError payload) for diagnosis"],"tags":["storage","panic","fjall","spawn-blocking"],"backgroundTag":"internal-invariant-violation","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"}