{"record":{"id":"9102631c77e66c13","repo":"affaan-m/ECC","slug":"cannot-merge-active-session-while-it-is","errorCode":null,"errorMessage":"Cannot merge active session {} while it is {}","messagePattern":"Cannot merge active session (.+?) while it is (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"ecc2/src/session/manager.rs","lineNumber":1783,"sourceCode":"pub struct WorktreeRebaseOutcome {\n    pub session_id: String,\n    pub branch: String,\n    pub base_branch: String,\n    pub already_up_to_date: bool,\n}\n\npub async fn merge_session_worktree(\n    db: &StateStore,\n    id: &str,\n    cleanup_worktree: bool,\n) -> Result<WorktreeMergeOutcome> {\n    let session = resolve_session(db, id)?;\n\n    if matches!(\n        session.state,\n        SessionState::Pending | SessionState::Running | SessionState::Idle | SessionState::Stale\n    ) {\n        anyhow::bail!(\n            \"Cannot merge active session {} while it is {}\",\n            session.id,\n            session.state\n        );\n    }\n\n    let worktree = session\n        .worktree\n        .clone()\n        .ok_or_else(|| anyhow::anyhow!(\"Session {} has no attached worktree\", session.id))?;\n    let outcome = crate::worktree::merge_into_base(&worktree)?;\n\n    if cleanup_worktree {\n        crate::worktree::remove(&worktree)?;\n        db.clear_worktree(&session.id)?;\n    }\n\n    Ok(WorktreeMergeOutcome {","sourceCodeStart":1765,"sourceCodeEnd":1801,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/session/manager.rs#L1765-L1801","documentation":"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.","triggerScenarios":"Calling merge_session_worktree(db, id, cleanup) for a session in SessionState::Pending, Running, Idle, or Stale.","commonSituations":"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.","solutions":["Stop or complete the session before calling merge_session_worktree","Check session state is terminal (Completed, Failed, or Cancelled) before attempting merge","For stale sessions, run the stale-session cleanup or mark them failed before merging","Wire the merge call to a post-completion hook so it only fires after the agent exits"],"exampleFix":"// before\nmerge_session_worktree(db, &id, true).await?;\n\n// after\nlet session = resolve_session(db, &id)?;\nif matches!(session.state, SessionState::Pending | SessionState::Running | SessionState::Idle | SessionState::Stale) {\n    return Err(anyhow!(\"Stop session {} before merging (current state: {})\", id, session.state));\n}\nmerge_session_worktree(db, &id, true).await?;","handlingStrategy":"type-guard","validationCode":"let session = resolve_session(db, id)?;\nif matches!(session.state, SessionState::Pending | SessionState::Running | SessionState::Idle | SessionState::Stale) {\n    return Err(anyhow!(\"Stop session {} (state: {}) before merging\", id, session.state));\n}","typeGuard":"fn is_mergeable(session: &Session) -> bool {\n    !matches!(\n        session.state,\n        SessionState::Pending | SessionState::Running | SessionState::Idle | SessionState::Stale\n    )\n}","tryCatchPattern":null,"preventionTips":["Only expose merge actions in the UI for sessions in terminal states","Call stop_session before merge in automated workflows","For stale sessions, run stale cleanup to transition them to a terminal state first"],"tags":["sessions","worktree","merge","lifecycle","state-machine"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}