Hmbown/CodeWhale · error
session ' ' is open in an interactive Codewhale session…
Error message
session '{session_id}' is open in an interactive Codewhale session; change it there instead — an external write would be reverted by its next autosave What it means
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.
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.
Example fix
// before: blind retry while TUI is open
set_session_archived(id, true)?;
// after: detect the conflict
if let Err(e) = set_session_archived(id, true) {
if e.kind() == std::io::ErrorKind::ResourceBusy {
eprintln!("close the open session '{id}' first");
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// before archiving/renaming externally, check the session is not live
fn session_is_live(lock_path: &Path) -> bool { lock_path.exists() } Try / catch
if let Err(e) = set_session_archived(id, true) {
if e.kind() == std::io::ErrorKind::ResourceBusy {
eprintln!("session '{id}' is open; change it in the interactive session");
} else { return Err(e.into()); }
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot open session : its queued input is already open in…
- control socket already live at
- A saved trace identifier collided. Try saving again.
- a window change is already in progress
- An existing Fleet artifact contains different bytes
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/4523abaa7cad44f2.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/session_manager.rs:445
}
/// Is this session currently owned by **this process's** interactive surface?
///
/// The registry is process-local. Reclamation must not treat a missing entry
/// here as proof that no other Codewhale process still owns the directory.
#[must_use]
pub fn is_live_session(session_id: &str) -> bool {
live_sessions()
.read()
.is_ok_and(|live| live.contains(session_id))
}
/// The error an external writer gets when the session is live.
///
/// `ResourceBusy` so callers can map it to a typed conflict rather than
/// pattern-matching on a message.
fn live_session_conflict(session_id: &str) -> std::io::Error {
std::io::Error::new(
std::io::ErrorKind::ResourceBusy,
format!(
"session '{session_id}' is open in an interactive Codewhale session; \
change it there instead — an external write would be reverted by its next autosave"
),
)
}
/// File-name stem of the sidecar mapping session ids to the session
/// instance (process boot) that created their persisted record. Lives in
/// the sessions directory next to the `<id>.json` records it describes.
const SESSION_BOOT_OWNERS_STEM: &str = "session_boot_owners";
static SESSION_BOOT_ID: std::sync::OnceLock<String> = std::sync::OnceLock::new();
/// Identity of this running session instance (one per process boot).
///
/// Mirrors the `SubAgentManager` boot id from #405: persisted records areView on GitHub (pinned to 73e0f67d83)