affaan-m/ECC · error · anyhow::Error

Cannot delete active session {} while it is {}

Error message

Cannot delete active session {} while it is {}

What it means

The delete_session function refuses to delete a session in an active state (Pending, Running, or Idle). Deleting an active session would orphan a running agent process and leave resources dangling. Notably, Stale sessions are NOT blocked — stale sessions can be deleted because the heartbeat has expired, suggesting the process is likely dead.

Source

Thrown at ecc2/src/session/manager.rs:2314

            dirty.push(entry.session_id.clone());
        } else if entry.worktree_health == worktree::WorktreeHealth::Conflicted {
            conflicted.push(entry.session_id.clone());
        } else {
            queue_blocked.push(entry.session_id.clone());
        }
    }

    (active, conflicted, dirty, queue_blocked)
}

pub async fn delete_session(db: &StateStore, id: &str) -> Result<()> {
    let session = resolve_session(db, id)?;

    if matches!(
        session.state,
        SessionState::Pending | SessionState::Running | SessionState::Idle
    ) {
        anyhow::bail!(
            "Cannot delete active session {} while it is {}",
            session.id,
            session.state
        );
    }

    if let Some(worktree) = session.worktree.as_ref() {
        let _ = crate::worktree::remove(worktree);
    }

    db.delete_session(&session.id)?;
    Ok(())
}

fn agent_program(cfg: &Config, agent_type: &str) -> Result<PathBuf> {
    let harness = HarnessKind::from_agent_type(agent_type);
    let runner_key = SessionHarnessInfo::runner_key(agent_type);
    if let Some(runner) = cfg.harness_runner(&runner_key) {

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Call stop_session first to transition the session to a terminal state, then delete
  2. Check that session.state is not Pending, Running, or Idle before calling delete_session
  3. For stuck sessions, force-stop or mark as failed before deletion
  4. Stale sessions can be deleted directly — wait for the heartbeat to expire if the process is unresponsive

Example fix

// before
delete_session(db, &id).await?;

// after
let session = resolve_session(db, &id)?;
if matches!(session.state, SessionState::Pending | SessionState::Running | SessionState::Idle) {
    stop_session(db, &id).await?;
}
delete_session(db, &id).await?;
Defensive patterns

Strategy: type-guard

Validate before calling

let session = resolve_session(db, id)?;
if matches!(session.state, SessionState::Pending | SessionState::Running | SessionState::Idle) {
    stop_session(db, id).await?;
}

Type guard

fn is_deletable(session: &Session) -> bool {
    !matches!(
        session.state,
        SessionState::Pending | SessionState::Running | SessionState::Idle
    )
}

Prevention

When it happens

Trigger: Calling delete_session(db, id) for a session in SessionState::Pending, Running, or Idle. (Stale sessions are allowed to be deleted.)

Common situations: User deletes a session that is still running or pending. Automated cleanup script that does not check state. Session appears hung but is technically in Idle state between operations.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/de97b9cfd61fc0ac. Report an issue: GitHub.