{"record":{"id":"2d6463737240ef0b","repo":"stablyai/orca","slug":"pty-provider-unavailable-for-worktree-deletion","errorCode":null,"errorMessage":"PTY provider unavailable for worktree deletion: ${worktreeId}","messagePattern":"PTY provider unavailable for worktree deletion: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/worktrees.ts","lineNumber":171,"sourceCode":"  worktreeId: string\n  hostId?: ExecutionHostId\n  force?: boolean\n  /** Explicit Force Delete only — `force` alone is set by the ordinary confirmation (#11960). */\n  allowUnverifiedPtyStop?: boolean\n  skipArchive?: boolean\n}\n\ntype DetectedWorktreeRequestArgs = { repoId: string } | ListDetectedWorktreesArgs\n\nasync function stopPtysForDestructiveWorktreeRemoval(\n  runtime: OrcaRuntimeService,\n  worktreeId: string,\n  options: { connectionId?: string; allowUnverifiedStop?: boolean } = {}\n): Promise<void> {\n  const { connectionId, allowUnverifiedStop } = options\n  const provider = connectionId ? getSshPtyProvider(connectionId) : getLocalPtyProvider()\n  if (!provider) {\n    throw new Error(`PTY provider unavailable for worktree deletion: ${worktreeId}`)\n  }\n  const teardownResult = await killAllProcessesForWorktree(worktreeId, {\n    runtime,\n    // Why: `repoId::path` ids repeat across hosts, so an unfenced sweep stops a same-id\n    // workspace's terminals on another connection — and the selector lookup this replaces\n    // throws `selector_ambiguous` the moment two hosts own the id.\n    resolvedWorktreeId: worktreeId,\n    ...(connectionId ? { resolvedConnectionId: connectionId } : {}),\n    localProvider: provider,\n    onPtyStopped: clearProviderPtyState,\n    requirePhysicalStop: true,\n    // Why (#11960): set only by an explicit Force Delete, never by the ordinary\n    // confirmation — otherwise the gate would be off on the primary delete path.\n    ...(allowUnverifiedStop ? { allowUnverifiedStop: true } : {}),\n    ...(connectionId ? { includeLocalRegistry: false } : {})\n  })\n  const total =\n    teardownResult.runtimeStopped + teardownResult.providerStopped + teardownResult.registryStopped","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/worktrees.ts#L153-L189","documentation":"During destructive worktree removal, Orca resolves the PTY provider to stop all running processes for the worktree before deleting it. For a remote (SSH) host it calls getSshPtyProvider(connectionId); for local it calls getLocalPtyProvider(). If the resolved provider is undefined, it cannot kill the worktree's terminals and refuses to proceed with a destructive delete, naming the worktreeId in the message. This is a safety gate: deleting without stopping PTYs would orphan processes.","triggerScenarios":"A worktree delete (or Force Delete) calling stopPtysForDestructiveWorktreeRemoval where connectionId is set but getSshPtyProvider returns undefined, or connectionId is absent and getLocalPtyProvider returns undefined. Reached at worktrees.ts:171.","commonSituations":"SSH connection dropped before the delete ran (no SSH PTY provider registered for that connectionId); local PTY subsystem not initialized yet at startup; connectionId stale after a reconnect that didn't re-register the provider; provider unregistered by a teardown racing the delete.","solutions":["For SSH deletes: reconnect the host so the SSH PTY provider is registered, then retry the delete.","For local deletes: ensure the local PTY subsystem has initialized (restart Orca if it failed to boot).","If the worktree truly has no running processes and the connection is gone, use Force Delete with allowUnverifiedStop so the gate is bypassed (the provider check still requires a provider, so reconnect first).","Verify the connectionId passed to the delete matches a currently-registered connection."],"exampleFix":"// before\nconst provider = connectionId ? getSshPtyProvider(connectionId) : getLocalPtyProvider()\nif (!provider) {\n  throw new Error(`PTY provider unavailable for worktree deletion: ${worktreeId}`)\n}\n\n// after — distinguish local vs ssh and hint the recovery\nif (!provider) {\n  const where = connectionId ? `SSH connection ${connectionId}` : 'local PTY subsystem'\n  throw new Error(`Cannot delete ${worktreeId}: no PTY provider for ${where}. Reconnect the host (SSH) or restart Orca (local) and retry.`)\n}","handlingStrategy":"validation","validationCode":"// Before delete: ensure a PTY provider is available\nconst provider = connectionId ? getSshPtyProvider(connectionId) : getLocalPtyProvider()\nif (!provider) {\n  if (connectionId) await reconnectSsh(connectionId)\n  const retry = connectionId ? getSshPtyProvider(connectionId) : getLocalPtyProvider()\n  if (!retry) return { ok: false, error: `No PTY provider for ${worktreeId}. Reconnect host or restart Orca.` }\n}","typeGuard":"function isPtyProvider(provider: unknown): provider is PtyProvider {\n  return !!provider && typeof (provider as any).stop === 'function'\n}","tryCatchPattern":"catch (err) {\n  if (err instanceof Error && err.message.startsWith('PTY provider unavailable for worktree deletion')) {\n    if (connectionId) await reconnectSsh(connectionId)\n    return retryDelete()\n  }\n  throw err\n}","preventionTips":["Reconnect SSH hosts before deleting remote worktrees so the SSH PTY provider is registered.","Ensure the local PTY subsystem initialized at startup (restart Orca if it didn't).","Verify the connectionId passed to delete matches a currently registered connection.","Use Force Delete only after confirming no live processes, and only after a provider is available."],"tags":["worktree-delete","pty","ssh","local","teardown"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}