stablyai/orca · warning · CodexAppServerUnsupportedError

codex CLI does not support the app-server subcommand: ${stde

Error message

codex CLI does not support the app-server subcommand: ${stderrTail.trim().slice(0, 400)}

What it means

Thrown as CodexAppServerUnsupportedError in the session's outer catch when an error escaped the body that is NOT already a typed unsupported/timeout error, but the captured stderr tail matches stderrIndicatesMissingAppServer. This upgrades a generic early-exit/spawn failure into the typed unsupported signal when codex's own stderr proves the subcommand is absent — so the capability cache can suppress future probes.

Source

Thrown at src/main/codex/codex-app-server-session.ts:294

  try {
    const session = async (): Promise<T> => {
      await requestRpc('initialize', {
        clientInfo: { name: 'orca_desktop', title: 'Orca', version: '0.0.0' }
      })
      notify('initialized')
      return body({ request: requestRpc, notify })
    }
    // Why: the timeout owns the whole callback, including time between RPCs;
    // killing the child alone cannot settle a callback awaiting unrelated work.
    return await Promise.race([session(), deadlinePromise])
  } catch (error) {
    if (
      error instanceof Error &&
      !(error instanceof CodexAppServerUnsupportedError) &&
      !(error instanceof CodexAppServerTimeoutError) &&
      stderrIndicatesMissingAppServer(stderrTail)
    ) {
      throw new CodexAppServerUnsupportedError(
        `codex CLI does not support the app-server subcommand: ${stderrTail.trim().slice(0, 400)}`
      )
    }
    throw error
  } finally {
    try {
      child.stdin.end()
    } catch {
      // stdin may already be destroyed after a kill; reaping below still runs.
    }
    if (!exited) {
      // Why: the server exits promptly on stdin EOF; the grace period only
      // bounds a wedged child before the guaranteed SIGKILL reap.
      await waitForProcessExitUntil(exitPromise, 1500)
      if (!exited) {
        killCodexAppServerProcessTree(child)
        await waitForProcessExitUntil(exitPromise, 1000)
      }

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Run: codex app-server (if it errors with an unknown-subcommand message, upgrade codex).
  2. Ensure PATH or resolveCodexCommand points at a current codex binary.
  3. Once thrown, let the capability cache mark this host unsupported so probes stop.
  4. Keep the host on the managed-home lane (the real-home installer falls back to 'unavailable').
  5. If using a wrapper script, confirm it forwards argv including 'app-server'.
Defensive patterns

Strategy: type-guard

Validate before calling

// Cheap pre-check: does 'codex app-server' parse?
// spawnSync(codex, ['app-server', '--help']) and inspect exit/stderr once per host; cache it.

Type guard

import { isCodexAppServerUnsupportedError } from './codex-app-server-session'
// This error is CodexAppServerUnsupportedError (thrown from the outer catch upgrade).

Try / catch

try {
  await runCodexAppServerSession(invocation, body)
} catch (error) {
  if (isCodexAppServerUnsupportedError(error)) {
    capabilityCache.setUnsupported(host, 'app-server-subcommand')
    // stay on managed lane permanently until codex upgrade
  } else {
    throw error
  }
}

Prevention

When it happens

Trigger: The codex child exited before completing initialize (or never started the JSON-RPC loop) and printed a 'no such subcommand: app-server' style message to stderr; the body threw a plain Error whose message didn't carry the unsupported signal, but stderr does.

Common situations: Old codex build where 'codex app-server' is rejected at the CLI parser; a codex wrapper script that doesn't forward the subcommand; a PATH resolution that picks up a different (older) codex binary.

Related errors


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