Hmbown/CodeWhale · warning · io::Error

session was deleted

Error message

session was deleted

What it means

retired_session_write_error() constructs the canonical NotFound io::Error returned when a write path targets a session whose accounting was already retired (deleted). The admission check under the session's write lock sees the deleted marker and aborts the operation instead of resurrecting the files. Callers receive NotFound("session was deleted").

Solutions

  1. Treat io::ErrorKind::NotFound as an expected outcome: drop the pending write for that session instead of retrying.
  2. Re-check session existence before issuing writes and refresh your session list after deletions.
  3. Serialize deletes and writes through with_session_write_admission / with_live_session_origin so retirement is observed before the callback runs.
  4. If you need the data, restore the session from backup rather than recreating files by hand — manual recreation will not match the deletion marker.

Example fix

// before
persist_late_usage_record(&id, ...)?;
// after
if let Err(e) = persist_late_usage_record(&id, ...) {
    if e.kind() == std::io::ErrorKind::NotFound { return Ok(()); } // session retired
    return Err(e);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before writing, confirm the session still exists
if !manager.session_exists(session_id) { return; } // skip write for retired session

Type guard

fn is_session_retired(err: &std::io::Error) -> bool {
    err.kind() == std::io::ErrorKind::NotFound && err.to_string().contains("session was deleted")
}

Try / catch

match manager.persist_late_usage_record(session_id, turn_id, source_id, ...) {
    Ok(_) => {},
    Err(e) if is_session_retired(&e) => { /* expected: session deleted concurrently, drop the write */ }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Persisting or appending a late-usage record (or calling with_live_session_origin / persist_late_usage_record) for a session_id whose ledger/state files have been deleted concurrently or earlier by session cleanup.

Common situations: A background turn finishing after the user deleted the session; two tasks racing where one deletes the session while another still holds its id; retrying a queued write against a session removed during app shutdown.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/8f93b6ae78fc26e7. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/session_manager.rs:1562

            return Ok(None);
        }
        write().map(Some)
    }

    /// Serialize active accounting admission with deletion of its origin.
    /// A retired origin is handled without running the callback. Callers must
    /// release this boundary before attempting a late-usage append, which
    /// independently checks retirement under the same stable lock.
    pub(crate) fn with_live_session_origin(
        &self,
        session_id: &str,
        accept: impl FnOnce() -> bool,
    ) -> io::Result<Option<bool>> {
        self.with_session_write_admission(session_id, || Ok(accept()))
    }

    fn retired_session_write_error() -> io::Error {
        io::Error::new(io::ErrorKind::NotFound, "session was deleted")
    }

    fn persist_late_usage_record(
        &self,
        session_id: &str,
        turn_id: &str,
        source_id: &str,
        route: &crate::cost_status::EffectiveRouteEnvelope,
        usage: Option<&codewhale_models::Usage>,
    ) -> io::Result<bool> {
        let (path, lock_path) = self.ensure_late_usage_paths(session_id)?;
        let lock_file = open_private_lock_file(&lock_path)?;
        let mut lock = fd_lock::RwLock::new(lock_file);
        let _guard = lock.write()?;
        if Self::late_usage_is_deleted(&path)? {
            // Handled, rather than a failed sink that should queue a retry.
            return Ok(true);
        }

View on GitHub (pinned to 73e0f67d83)