stablyai/orca · error · Error

PTY provider unavailable for worktree deletion: ${worktreeId

Error message

PTY provider unavailable for worktree deletion: ${worktreeId}

What it means

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.

Source

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

  worktreeId: string
  hostId?: ExecutionHostId
  force?: boolean
  /** Explicit Force Delete only — `force` alone is set by the ordinary confirmation (#11960). */
  allowUnverifiedPtyStop?: boolean
  skipArchive?: boolean
}

type DetectedWorktreeRequestArgs = { repoId: string } | ListDetectedWorktreesArgs

async function stopPtysForDestructiveWorktreeRemoval(
  runtime: OrcaRuntimeService,
  worktreeId: string,
  options: { connectionId?: string; allowUnverifiedStop?: boolean } = {}
): Promise<void> {
  const { connectionId, allowUnverifiedStop } = options
  const provider = connectionId ? getSshPtyProvider(connectionId) : getLocalPtyProvider()
  if (!provider) {
    throw new Error(`PTY provider unavailable for worktree deletion: ${worktreeId}`)
  }
  const teardownResult = await killAllProcessesForWorktree(worktreeId, {
    runtime,
    // Why: `repoId::path` ids repeat across hosts, so an unfenced sweep stops a same-id
    // workspace's terminals on another connection — and the selector lookup this replaces
    // throws `selector_ambiguous` the moment two hosts own the id.
    resolvedWorktreeId: worktreeId,
    ...(connectionId ? { resolvedConnectionId: connectionId } : {}),
    localProvider: provider,
    onPtyStopped: clearProviderPtyState,
    requirePhysicalStop: true,
    // Why (#11960): set only by an explicit Force Delete, never by the ordinary
    // confirmation — otherwise the gate would be off on the primary delete path.
    ...(allowUnverifiedStop ? { allowUnverifiedStop: true } : {}),
    ...(connectionId ? { includeLocalRegistry: false } : {})
  })
  const total =
    teardownResult.runtimeStopped + teardownResult.providerStopped + teardownResult.registryStopped

View on GitHub (pinned to 1136503c6a)

Solutions

  1. For SSH deletes: reconnect the host so the SSH PTY provider is registered, then retry the delete.
  2. For local deletes: ensure the local PTY subsystem has initialized (restart Orca if it failed to boot).
  3. 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).
  4. Verify the connectionId passed to the delete matches a currently-registered connection.

Example fix

// before
const provider = connectionId ? getSshPtyProvider(connectionId) : getLocalPtyProvider()
if (!provider) {
  throw new Error(`PTY provider unavailable for worktree deletion: ${worktreeId}`)
}

// after — distinguish local vs ssh and hint the recovery
if (!provider) {
  const where = connectionId ? `SSH connection ${connectionId}` : 'local PTY subsystem'
  throw new Error(`Cannot delete ${worktreeId}: no PTY provider for ${where}. Reconnect the host (SSH) or restart Orca (local) and retry.`)
}
Defensive patterns

Strategy: validation

Validate before calling

// Before delete: ensure a PTY provider is available
const provider = connectionId ? getSshPtyProvider(connectionId) : getLocalPtyProvider()
if (!provider) {
  if (connectionId) await reconnectSsh(connectionId)
  const retry = connectionId ? getSshPtyProvider(connectionId) : getLocalPtyProvider()
  if (!retry) return { ok: false, error: `No PTY provider for ${worktreeId}. Reconnect host or restart Orca.` }
}

Type guard

function isPtyProvider(provider: unknown): provider is PtyProvider {
  return !!provider && typeof (provider as any).stop === 'function'
}

Try / catch

catch (err) {
  if (err instanceof Error && err.message.startsWith('PTY provider unavailable for worktree deletion')) {
    if (connectionId) await reconnectSsh(connectionId)
    return retryDelete()
  }
  throw err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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