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

Cannot merge active session {} while it is {}

Error message

Cannot merge active session {} while it is {}

What it means

The merge_session_worktree function refuses to merge a session whose state is still active (Pending, Running, Idle, or Stale). Merging an active session's worktree could corrupt in-flight work since the agent process may still be writing files. The session must reach a terminal state (Completed, Failed, Cancelled) before merging is safe.

Source

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

pub struct WorktreeRebaseOutcome {
    pub session_id: String,
    pub branch: String,
    pub base_branch: String,
    pub already_up_to_date: bool,
}

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

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

    let worktree = session
        .worktree
        .clone()
        .ok_or_else(|| anyhow::anyhow!("Session {} has no attached worktree", session.id))?;
    let outcome = crate::worktree::merge_into_base(&worktree)?;

    if cleanup_worktree {
        crate::worktree::remove(&worktree)?;
        db.clear_worktree(&session.id)?;
    }

    Ok(WorktreeMergeOutcome {

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Stop or complete the session before calling merge_session_worktree
  2. Check session state is terminal (Completed, Failed, or Cancelled) before attempting merge
  3. For stale sessions, run the stale-session cleanup or mark them failed before merging
  4. Wire the merge call to a post-completion hook so it only fires after the agent exits

Example fix

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

// after
let session = resolve_session(db, &id)?;
if matches!(session.state, SessionState::Pending | SessionState::Running | SessionState::Idle | SessionState::Stale) {
    return Err(anyhow!("Stop session {} before merging (current state: {})", id, session.state));
}
merge_session_worktree(db, &id, true).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 | SessionState::Stale) {
    return Err(anyhow!("Stop session {} (state: {}) before merging", id, session.state));
}

Type guard

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

Prevention

When it happens

Trigger: Calling merge_session_worktree(db, id, cleanup) for a session in SessionState::Pending, Running, Idle, or Stale.

Common situations: User clicks 'merge' on a session that hasn't finished. Session appears idle but is between tool calls. Stale sessions (heartbeat expired) are still considered active for merge safety.

Related errors


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