stablyai/orca · warning · Error
Worktree deletion already in progress: ${args.worktreeId}
Error message
Worktree deletion already in progress: ${args.worktreeId} What it means
Thrown by worktrees:remove when worktreeRemovalsInFlight already has an entry for the same inFlightKey (worktreeId+hostId) but with a different optionsKey (options encode force/branch-delete/pty-stop flags). Same-options concurrent calls share the promise; differing-options concurrent calls are rejected to prevent two conflicting destructive operations on one worktree.
Source
Thrown at src/main/ipc/worktrees.ts:2376
ipcMain.handle(
'worktrees:remove',
async (_event, args: RemoveWorktreeArgs): Promise<RemoveWorktreeResult> => {
const { repoId, worktreePath } = parseWorktreeId(args.worktreeId)
const repo = getRepoForWorktreeRemoval(store, repoId, args.hostId)
if (!repo) {
throw new Error(`Repo not found: ${repoId}`)
}
// The resolved repo supplies host ownership when legacy callers omit args.hostId.
const removalHostId = getRepoExecutionHostId(repo)
const inFlightKey = getWorktreeRemovalInFlightKey(args.worktreeId, removalHostId)
const optionsKey = getWorktreeRemovalOptionsKey(args)
const inFlightRemoval = worktreeRemovalsInFlight.get(inFlightKey)
if (inFlightRemoval) {
if (inFlightRemoval.optionsKey === optionsKey) {
return inFlightRemoval.promise
}
throw new Error(`Worktree deletion already in progress: ${args.worktreeId}`)
}
// Why: concurrent stale-toast/double-click/sidebar races can hit the same worktree; share the op so only one path touches Git and disk.
const removal = (async (): Promise<RemoveWorktreeResult> => {
// Why: worktree.create is traced; delete freezes were invisible without a matching worktree.remove parent span.
return withWorktreeSpan({ stage: 'remove', path: worktreePath }, async () => {
if (isFolderRepo(repo)) {
if (args.worktreeId === getFolderWorkspaceRootId(repo)) {
throw new Error(
'Cannot delete the project root workspace. Remove the folder project instead.'
)
}
// Why: folder workspaces share one root, so there's no Git remove step to close shells; sweep PTYs before dropping metadata.
await withWorktreeRemoveStageSpan('pty_sweep', 'folder', async () => {
// Folder projects can be SSH-backed, so fence the sweep to the owning host exactly
// like the git paths — the local inventory must never reach a remote workspace's id.
// The resolved repo is authoritative here: path-derived metadata is shared by
// same-id host copies and can describe a different owner's workspace.View on GitHub (pinned to 1136503c6a)
Solutions
- De-dupe remove requests in the renderer (track an in-flight flag per worktreeId) and avoid changing options between retries.
- If a different option set is genuinely needed, wait for the first removal to settle, then re-evaluate from the new worktree state.
- Reuse the same options when retrying so the in-flight promise is shared instead of rejected.
Defensive patterns
Strategy: validation
Validate before calling
// De-dupe removes per worktreeId in the renderer.
const removing = new Set<string>()
async function removeOnce(worktreeId, opts) {
if (removing.has(worktreeId)) return
removing.add(worktreeId)
try {
await invoke('worktrees:remove', { worktreeId, ...opts })
} finally {
removing.delete(worktreeId)
}
} Try / catch
try {
await invoke('worktrees:remove', args)
} catch (e) {
if (e.message.startsWith('Worktree deletion already in progress')) {
// Another option set is running; await worktree refresh instead of retrying with different options.
return
}
throw e
} Prevention
- Track an in-flight flag per worktreeId in the renderer.
- Keep options stable across automatic retries.
- Suppress double-click-triggered duplicate removes.
When it happens
Trigger: Two near-simultaneous worktrees:remove calls for the same worktree with different options — e.g. a non-force delete in flight and a force delete arriving, or a sidebar double-click racing a stale toast's force action.
Common situations: Double-click in the sidebar, a stale recovery toast firing while the user retries with force, or an automation client issuing overlapping remove requests with changed flags.
Related errors
- A clone is already in progress for this SSH destination
- Project host setup not found: ${args.setupId}
- No preserved branch cleanup is pending for "${branchName}".
- Repo not found: ${repoId}
- Cannot delete the project root workspace. Remove the folder
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/57bb744544b5891e.
Report an issue: GitHub.