stablyai/orca · error · Error

No preserved branch cleanup is pending for "${branchName}".

Error message

No preserved branch cleanup is pending for "${branchName}".

What it means

Thrown by getPreservedBranchCleanupTarget inside the worktrees:forceDeletePreservedBranch handler. It looks up the in-memory pending cleanup target (keyed by worktreeId+hostId, with a legacy branch+head match fallback) and requires the stored branchName and head to equal the request's branchName and expectedHead exactly. The pending-targets map is process-local and not persisted, so it is empty after any main-process restart.

Source

Thrown at src/main/ipc/worktrees.ts:596

  worktreeId: string,
  branchName: string,
  expectedHead: string,
  hostId?: ExecutionHostId
): PreservedBranchCleanupTarget {
  const exactTarget = hostId
    ? preservedBranchCleanupByScope.get(preservedBranchCleanupScopeKey({ worktreeId, hostId }))
    : undefined
  const legacyMatches = hostId
    ? []
    : [...preservedBranchCleanupByScope.values()].filter(
        (target) =>
          target.worktreeId === worktreeId &&
          target.branchName === branchName &&
          target.head === expectedHead
      )
  const target = exactTarget ?? (legacyMatches.length === 1 ? legacyMatches[0] : undefined)
  if (!target || target.branchName !== branchName || target.head !== expectedHead) {
    throw new Error(`No preserved branch cleanup is pending for "${branchName}".`)
  }
  return target
}

const loggedUnavailableSshGitProviders = new Set<string>()
const loggedWorktreeListFailures = new Set<string>()
const loggedMalformedWorktreeMetaKeys = new Set<string>()
export const DETECTED_WORKTREE_PROVIDER_TIMEOUT_MS = 30_000
export const LINEAGE_HYDRATION_TIMEOUT_MS = 5_000
// Why: absorb renderer polling bursts while bounding external worktree-change lag to one short refresh window.
const DETECTED_WORKTREE_SCAN_CACHE_TTL_MS = 5_000

type DetectedWorktreeScanCacheEntry = {
  expiresAt: number
  worktrees: GitWorktreeInfo[]
}

type DetectedWorktreeScan = {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Only call forceDeletePreservedBranch with the exact branchName/expectedHead/hostId returned by the preceding worktrees:remove result.
  2. If the main process restarted, the preserved branch is no longer tracked — delete the branch via normal Git tooling instead.
  3. Pass the same hostId that was used for removal so the scope-keyed lookup hits.
Defensive patterns

Strategy: validation

Validate before calling

// Only offer force-delete when the remove result actually preserved a branch.
if (removeResult?.preservedBranch?.head) {
  await invoke('worktrees:forceDeletePreservedBranch', {
    worktreeId,
    branchName: removeResult.preservedBranch.branchName,
    expectedHead: removeResult.preservedBranch.head,
    hostId: removalHostId
  })
}

Try / catch

try {
  await invoke('worktrees:forceDeletePreservedBranch', payload)
} catch (e) {
  if (e.message.startsWith('No preserved branch cleanup is pending')) {
    // Map cleared (e.g. restart) or already deleted — fall back to manual Git branch deletion.
  } else throw e
}

Prevention

When it happens

Trigger: Calling worktrees:forceDeletePreservedBranch when no cleanup was registered (e.g. after an app restart), with a different hostId than the removal used, with a stale expectedHead, for a branch already force-deleted, or for a branch that was never preserved on delete.

Common situations: User closes/reopens Orca between removing a worktree and confirming the force-delete prompt; renderer retries a stale toast action; or the renderer passes args.hostId that doesn't match the host that owned the removal (so the exact scope key misses and the legacy match is ambiguous/absent).

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/e3d0b3308d7daf3b. Report an issue: GitHub.