stablyai/orca · warning

startup_terminal_missing

startup_terminal_missing

Error message

startup_terminal_missing

What it means

Thrown when a worktree's setup script is configured to launch in a split pane (setupScriptLaunchMode = 'split-vertical' | 'split-horizontal') but no startup terminal handle exists to split from. The split operation needs a parent terminal; the handle is captured when the startup terminal is created. If that terminal was never created (e.g. no startup command was produced for this worktree), the handle is null and the split is impossible. The throw is a guard so splitTerminal never receives undefined.

Source

Thrown at src/main/ipc/worktree-remote.ts:352

  let didSpawnSetup = false
  if (setup) {
    try {
      const setupCommand =
        wrappedSetupCommandStr ??
        buildSetupRunnerCommand(
          setup.runnerScriptPath,
          getSetupRunnerCommandPlatformForLaunch(
            setup,
            process.platform === 'win32' ? 'windows' : 'posix'
          ),
          setup.shell
        )
      const setupLaunchMode =
        (settings as Partial<Pick<GlobalSettings, 'setupScriptLaunchMode'>>)
          .setupScriptLaunchMode ?? 'new-tab'
      if (setupLaunchMode === 'split-vertical' || setupLaunchMode === 'split-horizontal') {
        if (!startupTerminalHandle) {
          throw new Error('startup_terminal_missing')
        }
        await runtime.splitTerminal(startupTerminalHandle, {
          direction: setupLaunchMode === 'split-horizontal' ? 'horizontal' : 'vertical',
          command: setupCommand,
          env: setup.envVars,
          activate: false
        })
      } else {
        await runtime.createTerminal(`id:${worktree.id}`, {
          title: 'Setup',
          command: setupCommand,
          env: setup.envVars,
          activate: false
        })
      }
      didSpawnSetup = true
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error)

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Set setupScriptLaunchMode back to 'new-tab' in Settings so the setup script gets its own terminal instead of splitting a nonexistent one.
  2. Ensure the repo/worktree has a real startup command so a startup terminal is created (the handle is captured at line 322).
  3. If splitting is required, verify the startup terminal actually spawned before the setup block runs; treat a missing handle as a degraded 'new-tab' fallback rather than throwing.

Example fix

// before
if (!startupTerminalHandle) {
  throw new Error('startup_terminal_missing')
}
await runtime.splitTerminal(startupTerminalHandle, { ... })

// after — degrade to a new tab when no terminal exists to split
if (!startupTerminalHandle) {
  await runtime.createTerminal(`id:${worktree.id}`, { title: 'Setup', command: setupCommand, env: setup.envVars, activate: false })
} else {
  await runtime.splitTerminal(startupTerminalHandle, { ... })
}
Defensive patterns

Strategy: validation

Validate before calling

// Before create: ensure split mode has a startup terminal to split from
const mode = settings.setupScriptLaunchMode ?? 'new-tab'
const isSplit = mode === 'split-vertical' || mode === 'split-horizontal'
if (isSplit && !hasStartupCommandForWorktree(repo, worktree)) {
  // downgrade instead of letting the main process throw
  effectiveMode = 'new-tab'
}

Type guard

function canSplitSetup(
  mode: string | undefined,
  startupTerminalHandle: string | null
): startupTerminalHandle is string {
  return (mode === 'split-vertical' || mode === 'split-horizontal') && startupTerminalHandle !== null
}

Try / catch

// In the setup spawn block, treat a missing handle as degraded new-tab
catch (err) {
  if (err instanceof Error && err.message === 'startup_terminal_missing') {
    await runtime.createTerminal(`id:${worktree.id}`, { title: 'Setup', command: setupCommand, env: setup.envVars, activate: false })
    didSpawnSetup = true
  } else { throw err }
}

Prevention

When it happens

Trigger: A create/open flow that produces a setup script but no sequenced startup terminal (startup command empty or skipped), while GlobalSettings.setupScriptLaunchMode is set to split-vertical or split-horizontal. Reached inside spawnSetupForRemoteWorktreeCreate / the launch path at worktree-remote.ts:350-353.

Common situations: Repo hooks define a setup script but no startup command; user switched setupScriptLaunchMode to a split mode in Settings; agent/TUI launch path that bypasses the startup terminal; a partial state where the startup terminal creation earlier returned a warning (lines 327-332) but execution continued into the setup block.

Related errors


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