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

  1. Close the interactive Codewhale session first, then retry archive/rename from outside.
  2. Make the change inside the interactive session itself (archive/rename via the TUI).
  3. 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

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


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 are

View on GitHub (pinned to 73e0f67d83)