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

Session {} has no attached worktree

Error message

Session {} has no attached worktree

What it means

The merge_session_worktree function, after verifying the session is in a terminal state, requires an attached worktree to merge. If session.worktree is None, there is nothing to merge. This happens when the session was created without a worktree (e.g., a main-repo session) or the worktree was already removed.

Source

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

    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 {
        session_id: session.id,
        branch: outcome.branch,
        base_branch: outcome.base_branch,
        already_up_to_date: outcome.already_up_to_date,
        cleaned_worktree: cleanup_worktree,
    })
}

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

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Check session.worktree.is_some() before calling merge_session_worktree
  2. If the worktree was already cleaned up, treat the operation as a no-op success
  3. Ensure sessions that need merging are created with a worktree from the start
  4. Avoid calling merge twice on the same session — track which sessions have been merged

Example fix

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

// after
let session = resolve_session(db, &id)?;
if session.worktree.is_none() {
    tracing::info!("session {} has no worktree to merge", id);
    return Ok(WorktreeMergeOutcome::noop(id));
}
merge_session_worktree(db, &id, true).await?;
Defensive patterns

Strategy: validation

Validate before calling

let session = resolve_session(db, id)?;
if session.worktree.is_none() {
    return Ok(WorktreeMergeOutcome::noop(id));
}

Type guard

fn has_worktree(session: &Session) -> bool {
    session.worktree.is_some()
}

Try / catch

match merge_session_worktree(db, id, cleanup).await {
    Ok(outcome) => Ok(outcome),
    Err(e) if e.to_string().contains("no attached worktree") => {
        Ok(WorktreeMergeOutcome::noop(id))
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Calling merge_session_worktree on a session where session.worktree is None — the session was created without a worktree branch or the worktree was already cleaned up.

Common situations: Session ran in the main repository without a worktree. Worktree was already merged and removed by a previous call. Worktree path was manually deleted outside ECC.

Related errors


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