{"record":{"id":"addc3d6c0f06871e","repo":"Yeachan-Heo/oh-my-codex","slug":"worktree-rollback-failed","errorCode":"worktree_rollback_failed","errorMessage":"worktree_rollback_failed:${errors.join(' | ')}","messagePattern":"worktree_rollback_failed:(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/team/worktree.ts","lineNumber":547,"sourceCode":"    const stillCheckedOut = hasBranchInUse(entriesAfterRemove, result.branchName, result.worktreePath);\n    if (stillCheckedOut) continue;\n\n    try {\n      await execFilePromise('git', ['branch', '-D', result.branchName], {\n        cwd: result.repoRoot,\n        encoding: 'utf-8',\n      });\n    } catch (err: unknown) {\n      if (branchExists(result.repoRoot, result.branchName)) {\n        const stderr = ((err as Record<string, unknown>).stderr as string ?? '').trim();\n        const exitCode = (err as Record<string, unknown>).code;\n        errors.push(`delete_branch:${result.branchName}:${stderr || `exit_${exitCode}`}`);\n      }\n    }\n  }\n\n  if (errors.length > 0) {\n    throw new Error(`worktree_rollback_failed:${errors.join(' | ')}`);\n  }\n}\n\nexport async function removeWorktreeForce(repoRoot: string, worktreePath: string): Promise<void> {\n  await execFilePromise('git', ['worktree', 'remove', '--force', worktreePath], {\n    cwd: repoRoot,\n    encoding: 'utf-8',\n  });\n}\n","sourceCodeStart":529,"sourceCodeEnd":557,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/team/worktree.ts#L529-L557","documentation":"rollbackProvisionedWorktrees aggregates every individual cleanup failure (worktree removal, branch deletion, etc.) into an errors array; if any entry failed, it throws worktree_rollback_failed with all messages joined by ' | '. Callers are startTeam, shutdownTeam, rollbackScaleUp, and cleanupScaleDownResources — i.e. this fires during teardown/compensation paths, and individual component errors (like delete_branch:<name>:<stderr>) are collected rather than failing fast.","triggerScenarios":"Calling rollbackProvisionedWorktrees when at least one git operation in the rollback loop fails — e.g. `git branch -D` refuses because the branch is checked out elsewhere, or worktree remove fails because a process holds files open in the worktree directory.","commonSituations":"Teardown racing with still-running agent processes that hold files open in worktrees (Windows file locks especially); branches recreated or re-checked-out between provisioning and rollback; partial cleanup where an earlier failure leaves state the later steps depend on; shutdown during a concurrent scale-down already cleaning the same resources.","solutions":["Read each pipe-separated segment: delete_branch:<branch>:<stderr> tells you which git op and why; fix that underlying condition first","Ensure all processes with cwd inside the worktrees are terminated/killed before calling shutdown/rollback","Retry the rollback after manual cleanup: `git worktree remove --force <path>` and `git branch -D <branch>` for the listed items","Serialize rollback with scale-down cleanup (a lock around rollbackScaleUp/cleanupScaleDownResources) to avoid double-free of the same worktree"],"exampleFix":"// before\nawait shutdownTeam(team); // throws worktree_rollback_failed: delete_branch:foo:... \n\n// after\nawait stopAllAgentProcesses(team); // release file handles first\nexecSync('git worktree remove --force .worktrees/foo', { cwd: repoRoot });\nawait shutdownTeam(team);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await rollbackProvisionedWorktrees(repoRoot, worktrees);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('worktree_rollback_failed:')) {\n    const parts = e.message.slice('worktree_rollback_failed:'.length).split(' | ');\n    // each part: <op>:<target>:<stderr> — remediate then retry rollback for remaining items\n  }\n  throw e;\n}","preventionTips":["Kill all processes with cwd inside worktrees before teardown","Idempotent rollback: tolerate partially-cleaned state on retry","Avoid running shutdownTeam and cleanupScaleDownResources concurrently for the same resources"],"tags":["git","worktree","cleanup","teardown","aggregate"],"backgroundTag":"cleanup-rollback-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}