{"record":{"id":"c9ff083da8349126","repo":"Hmbown/CodeWhale","slug":"session-accounting-was-deleted","errorCode":null,"errorMessage":"session accounting was deleted","messagePattern":"session accounting was deleted","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"warning","filePath":"crates/tui/src/session_manager.rs","lineNumber":1655,"sourceCode":"        let lock_file = match open_private_read_file(&lock_path) {\n            Ok(file) => file,\n            Err(error) if error.kind() == io::ErrorKind::NotFound => {\n                // Atomic replacement makes a copied ledger readable without\n                // creating a lock. No writer can have published a tombstone\n                // without first creating the stable lock.\n                return read(&path);\n            }\n            Err(error) => return Err(error),\n        };\n        let lock = fd_lock::RwLock::new(lock_file);\n        let _guard = lock.read()?;\n        read(&path)\n    }\n\n    fn load_late_usage(&self, session_id: &str) -> io::Result<LateUsageLedger> {\n        self.with_session_read_lock(session_id, |path| {\n            if Self::late_usage_is_deleted(path)? {\n                return Err(io::Error::new(\n                    io::ErrorKind::NotFound,\n                    \"session accounting was deleted\",\n                ));\n            }\n            Self::load_late_usage_unlocked(path)\n        })\n    }\n\n    fn apply_late_usage_to_metadata(&self, metadata: &mut SessionMetadata) {\n        let ledger = match self.load_late_usage(&metadata.id) {\n            Ok(ledger) => ledger,\n            Err(_) => {\n                // The transcript is independent of optional accounting data.\n                // Keep a stable gap receipt even when this projection is later\n                // saved; loading it again must not invent another missing call.\n                let fingerprint = crate::cost_status::usage_source_fingerprint(&format!(\n                    \"late-usage-unavailable:{}\",\n                    crate::cost_status::usage_source_fingerprint(&metadata.id)","sourceCodeStart":1637,"sourceCodeEnd":1673,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/session_manager.rs#L1637-L1673","documentation":"load_late_usage found the session's deletion marker on disk before reading the ledger, so it returns NotFound(\"session accounting was deleted\") instead of loading a retired session's accounting. It distinguishes a deliberately deleted session from a merely missing file (which yields an empty/default ledger).","triggerScenarios":"Calling load_late_usage(session_id) after the session was deleted — late_usage_is_deleted(path) returned true under the read lock, indicating the tombstone/deleted state.","commonSituations":"A UI view or metrics aggregation still holds a stale session_id after the user deleted that session; restoring an old snapshot references a session whose accounting was removed.","solutions":["Handle ErrorKind::NotFound by treating the session's accounting as empty/absent and dropping references to the deleted session id.","Refresh the session list after deletions so stale ids are not reused for reads.","If the deletion was unintended, restore the session's accounting files from backup; do not just recreate empty files while the deleted marker persists.","Route reads through load_late_usage rather than opening the ledger path directly so the deleted state is honored."],"exampleFix":"// before\nlet ledger = manager.load_late_usage(&session_id)?;\n// after\nlet ledger = match manager.load_late_usage(&session_id) {\n    Ok(l) => l,\n    Err(e) if e.kind() == std::io::ErrorKind::NotFound => LateUsageLedger::default(), // deleted\n    Err(e) => return Err(e),\n};","handlingStrategy":"fallback","validationCode":"// before loading, check the session id is still known\nif !known_sessions.contains(session_id) { return LateUsageLedger::default(); }","typeGuard":"fn is_accounting_deleted(err: &std::io::Error) -> bool {\n    err.kind() == std::io::ErrorKind::NotFound && err.to_string().contains(\"session accounting was deleted\")\n}","tryCatchPattern":"let ledger = manager.load_late_usage(session_id).unwrap_or_else(|e| {\n    debug_assert!(is_accounting_deleted(&e) || e.kind() == std::io::ErrorKind::NotFound);\n    LateUsageLedger::default() // deleted session: treat as empty accounting\n});","preventionTips":["Drop cached session ids after deletion events; subscribe to session-list changes.","Always read accounting through load_late_usage so the deleted marker is honored.","Back up session accounting before deletion if the data may be needed again."],"tags":["not-found","deletion","tombstone","io","session-state"],"backgroundTag":"record-not-found","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}