{"record":{"id":"e3d0b3308d7daf3b","repo":"stablyai/orca","slug":"no-preserved-branch-cleanup-is-pending-for-bran","errorCode":null,"errorMessage":"No preserved branch cleanup is pending for \"${branchName}\".","messagePattern":"No preserved branch cleanup is pending for \"(.+?)\"\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/worktrees.ts","lineNumber":596,"sourceCode":"  worktreeId: string,\n  branchName: string,\n  expectedHead: string,\n  hostId?: ExecutionHostId\n): PreservedBranchCleanupTarget {\n  const exactTarget = hostId\n    ? preservedBranchCleanupByScope.get(preservedBranchCleanupScopeKey({ worktreeId, hostId }))\n    : undefined\n  const legacyMatches = hostId\n    ? []\n    : [...preservedBranchCleanupByScope.values()].filter(\n        (target) =>\n          target.worktreeId === worktreeId &&\n          target.branchName === branchName &&\n          target.head === expectedHead\n      )\n  const target = exactTarget ?? (legacyMatches.length === 1 ? legacyMatches[0] : undefined)\n  if (!target || target.branchName !== branchName || target.head !== expectedHead) {\n    throw new Error(`No preserved branch cleanup is pending for \"${branchName}\".`)\n  }\n  return target\n}\n\nconst loggedUnavailableSshGitProviders = new Set<string>()\nconst loggedWorktreeListFailures = new Set<string>()\nconst loggedMalformedWorktreeMetaKeys = new Set<string>()\nexport const DETECTED_WORKTREE_PROVIDER_TIMEOUT_MS = 30_000\nexport const LINEAGE_HYDRATION_TIMEOUT_MS = 5_000\n// Why: absorb renderer polling bursts while bounding external worktree-change lag to one short refresh window.\nconst DETECTED_WORKTREE_SCAN_CACHE_TTL_MS = 5_000\n\ntype DetectedWorktreeScanCacheEntry = {\n  expiresAt: number\n  worktrees: GitWorktreeInfo[]\n}\n\ntype DetectedWorktreeScan = {","sourceCodeStart":578,"sourceCodeEnd":614,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/worktrees.ts#L578-L614","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Only call forceDeletePreservedBranch with the exact branchName/expectedHead/hostId returned by the preceding worktrees:remove result.","If the main process restarted, the preserved branch is no longer tracked — delete the branch via normal Git tooling instead.","Pass the same hostId that was used for removal so the scope-keyed lookup hits."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Only offer force-delete when the remove result actually preserved a branch.\nif (removeResult?.preservedBranch?.head) {\n  await invoke('worktrees:forceDeletePreservedBranch', {\n    worktreeId,\n    branchName: removeResult.preservedBranch.branchName,\n    expectedHead: removeResult.preservedBranch.head,\n    hostId: removalHostId\n  })\n}","typeGuard":null,"tryCatchPattern":"try {\n  await invoke('worktrees:forceDeletePreservedBranch', payload)\n} catch (e) {\n  if (e.message.startsWith('No preserved branch cleanup is pending')) {\n    // Map cleared (e.g. restart) or already deleted — fall back to manual Git branch deletion.\n  } else throw e\n}","preventionTips":["Forward the exact branchName/expectedHead/hostId returned by the preceding remove.","Remember the pending-targets map is in-memory and empty after a restart.","Disable the force-delete affordance once the worktree state has changed."],"tags":["preserved-branch","ipc","worktree-remove","in-memory-state"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}