{"record":{"id":"4523abaa7cad44f2","repo":"Hmbown/CodeWhale","slug":"session-session-id-is-open-in-an-interactive-c","errorCode":null,"errorMessage":"session '{session_id}' is open in an interactive Codewhale session; change it there instead — an external write would be reverted by its next autosave","messagePattern":"session '(.+?)' is open in an interactive Codewhale session; change it there instead — an external write would be reverted by its next autosave","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/session_manager.rs","lineNumber":445,"sourceCode":"}\n\n/// Is this session currently owned by **this process's** interactive surface?\n///\n/// The registry is process-local. Reclamation must not treat a missing entry\n/// here as proof that no other Codewhale process still owns the directory.\n#[must_use]\npub fn is_live_session(session_id: &str) -> bool {\n    live_sessions()\n        .read()\n        .is_ok_and(|live| live.contains(session_id))\n}\n\n/// The error an external writer gets when the session is live.\n///\n/// `ResourceBusy` so callers can map it to a typed conflict rather than\n/// pattern-matching on a message.\nfn live_session_conflict(session_id: &str) -> std::io::Error {\n    std::io::Error::new(\n        std::io::ErrorKind::ResourceBusy,\n        format!(\n            \"session '{session_id}' is open in an interactive Codewhale session; \\\n             change it there instead — an external write would be reverted by its next autosave\"\n        ),\n    )\n}\n\n/// File-name stem of the sidecar mapping session ids to the session\n/// instance (process boot) that created their persisted record. Lives in\n/// the sessions directory next to the `<id>.json` records it describes.\nconst SESSION_BOOT_OWNERS_STEM: &str = \"session_boot_owners\";\n\nstatic SESSION_BOOT_ID: std::sync::OnceLock<String> = std::sync::OnceLock::new();\n\n/// Identity of this running session instance (one per process boot).\n///\n/// Mirrors the `SubAgentManager` boot id from #405: persisted records are","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/session_manager.rs#L427-L463","documentation":"live_session_conflict builds the io::Error returned when an external writer tries to change (archive, rename) a session that is currently open in an interactive Codewhale session. It uses ErrorKind::ResourceBusy so callers can map it to a typed conflict. The interactive session's next autosave would silently revert external writes, so mutation from outside is refused up front.","triggerScenarios":"Calling set_session_archived or rename_session for a session_id that is currently live (open in a running TUI session), from a different process or CLI context.","commonSituations":"A user archives/renames sessions from a script or second Codewhale instance while the same session is open in the TUI; cleanup scripts sweep sessions during active use; migrating session storage while a TUI is running.","solutions":["Close the interactive Codewhale session first, then retry archive/rename from outside.","Make the change inside the interactive session itself (archive/rename via the TUI).","In your caller, match on io::ErrorKind::ResourceBusy and surface 'session is open' as a conflict instead of retrying."],"exampleFix":"// before: blind retry while TUI is open\nset_session_archived(id, true)?;\n// after: detect the conflict\nif let Err(e) = set_session_archived(id, true) {\n    if e.kind() == std::io::ErrorKind::ResourceBusy {\n        eprintln!(\"close the open session '{id}' first\");\n    }\n}","handlingStrategy":"try-catch","validationCode":"// before archiving/renaming externally, check the session is not live\nfn session_is_live(lock_path: &Path) -> bool { lock_path.exists() }","typeGuard":null,"tryCatchPattern":"if let Err(e) = set_session_archived(id, true) {\n    if e.kind() == std::io::ErrorKind::ResourceBusy {\n        eprintln!(\"session '{id}' is open; change it in the interactive session\");\n    } else { return Err(e.into()); }\n}","preventionTips":["Close the TUI session before external mutation scripts run.","Perform archive/rename inside the interactive session.","Don't run parallel Codewhale instances on the same session store.","Map ResourceBusy to a typed conflict in your tooling."],"tags":["concurrency","io","conflict"],"backgroundTag":"invalid-state-transition","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"}