{"record":{"id":"0e8ee5f088e4f0b1","repo":"stablyai/orca","slug":"cannot-delete-the-project-root-workspace-remove-t","errorCode":null,"errorMessage":"Cannot delete the project root workspace. Remove the folder project instead.","messagePattern":"Cannot delete the project root workspace\\. Remove the folder project instead\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/worktrees.ts","lineNumber":2385,"sourceCode":"      // The resolved repo supplies host ownership when legacy callers omit args.hostId.\n      const removalHostId = getRepoExecutionHostId(repo)\n      const inFlightKey = getWorktreeRemovalInFlightKey(args.worktreeId, removalHostId)\n      const optionsKey = getWorktreeRemovalOptionsKey(args)\n      const inFlightRemoval = worktreeRemovalsInFlight.get(inFlightKey)\n      if (inFlightRemoval) {\n        if (inFlightRemoval.optionsKey === optionsKey) {\n          return inFlightRemoval.promise\n        }\n        throw new Error(`Worktree deletion already in progress: ${args.worktreeId}`)\n      }\n\n      // Why: concurrent stale-toast/double-click/sidebar races can hit the same worktree; share the op so only one path touches Git and disk.\n      const removal = (async (): Promise<RemoveWorktreeResult> => {\n        // Why: worktree.create is traced; delete freezes were invisible without a matching worktree.remove parent span.\n        return withWorktreeSpan({ stage: 'remove', path: worktreePath }, async () => {\n          if (isFolderRepo(repo)) {\n            if (args.worktreeId === getFolderWorkspaceRootId(repo)) {\n              throw new Error(\n                'Cannot delete the project root workspace. Remove the folder project instead.'\n              )\n            }\n            // Why: folder workspaces share one root, so there's no Git remove step to close shells; sweep PTYs before dropping metadata.\n            await withWorktreeRemoveStageSpan('pty_sweep', 'folder', async () => {\n              // Folder projects can be SSH-backed, so fence the sweep to the owning host exactly\n              // like the git paths — the local inventory must never reach a remote workspace's id.\n              // The resolved repo is authoritative here: path-derived metadata is shared by\n              // same-id host copies and can describe a different owner's workspace.\n              const ownerHost = parseExecutionHostId(removalHostId)\n              const sshPtyProvider =\n                ownerHost?.kind === 'ssh' ? getSshPtyProvider(ownerHost.targetId) : undefined\n              const externalHost = ownerHost?.kind === 'ssh' || ownerHost?.kind === 'runtime'\n              await killAllProcessesForWorktree(args.worktreeId, {\n                runtime,\n                resolvedWorktreeId: args.worktreeId,\n                ...(ownerHost?.kind === 'ssh' ? { resolvedConnectionId: ownerHost.targetId } : {}),\n                ...(ownerHost?.kind === 'runtime'","sourceCodeStart":2367,"sourceCodeEnd":2403,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/worktrees.ts#L2367-L2403","documentation":"Thrown by both worktrees:remove and worktrees:forgetLocal when the target worktreeId equals the folder workspace root id (getFolderWorkspaceRootId). Folder workspaces share a single root directory; deleting it would destroy the project itself, so removal is blocked and the user is told to remove the folder project instead.","triggerScenarios":"Issuing worktrees:remove or worktrees:forgetLocal with the folder project's root workspace id for a folder-mode repo (isFolderRepo true).","commonSituations":"UI inadvertently offers a delete action on the folder root row, or a remote/automation client targets the root id of a folder workspace.","solutions":["Hide/disable delete for the folder root workspace in the UI and route project removal through the folder-project removal flow.","For automation, detect folder repos and never call remove on getFolderWorkspaceRootId(repo).","If the user wants the workspace gone, remove the folder project via its dedicated removal API."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import { isFolderRepo } from '../../shared/folder-workspaces'\nimport { getFolderWorkspaceRootId } from '../../shared/folder-workspace-id'\n\nfunction canDeleteWorktree(repo, worktreeId) {\n  return !(isFolderRepo(repo) && worktreeId === getFolderWorkspaceRootId(repo))\n}","typeGuard":"function isFolderRootWorkspace(repo: unknown, worktreeId: string): boolean {\n  return !!repo && isFolderRepo(repo) && worktreeId === getFolderWorkspaceRootId(repo)\n}","tryCatchPattern":null,"preventionTips":["Never offer delete on the folder root workspace row.","Route folder-project removal through its dedicated API.","For automation, detect folder repos and exclude their root id from remove calls."],"tags":["folder-workspace","validation","ipc","worktree-remove"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}