moeru-ai/airi · error

The first `cap run` argument must be `ios` or `android`.

Error message

The first `cap run` argument must be `ios` or `android`.

What it means

Thrown by capVitePlugin() when options.capArgs[0] is not 'ios' or 'android'. Identical guard to runCapVite but inside the Vite plugin factory, which runs in the wrapper-config Vite process where capArgs arrive via the CAP_VITE_CAP_ARGS_JSON env var.

Source

Thrown at packages/cap-vite/src/vite-plugin.ts:107

      onRestart()
    }
  }

  process.stdin.on('keypress', onKeyPress)

  return () => {
    process.stdin.off('keypress', onKeyPress)

    if (shouldRestoreRawMode) {
      process.stdin.setRawMode(false)
    }
  }
}

export function capVitePlugin(options: CapVitePluginOptions): Plugin {
  const platform = parseCapacitorPlatform(options.capArgs[0])
  if (!platform) {
    throw new Error('The first `cap run` argument must be `ios` or `android`.')
  }
  const resolvedPlatform: CapacitorPlatform = platform

  return {
    apply: 'serve',
    name: 'cap-vite:run-capacitor',
    async configureServer(server) {
      const resolvedCapArgs = await resolveCapRunArgs(options.capArgs)
      const cwd = resolve(server.config.root)
      const platformRoot = resolve(cwd, resolvedPlatform)
      const debounceMs = 300
      const logger = server.config.logger

      let currentCapProcess: Result | undefined
      let restartTask: Promise<void> | undefined
      let queuedRestartReason: string | undefined
      let disposeShortcut: (() => void) | undefined
      let shuttingDown = false

View on GitHub (pinned to 27111382b4)

Solutions

  1. Do not invoke the wrapper config directly; go through runCapVite() which sets CAP_VITE_CAP_ARGS_JSON correctly.
  2. If you must set it manually, ensure CAP_VITE_CAP_ARGS_JSON is a JSON array like ["ios"] or ["android","--target","<id>"].
  3. Clear stale CAP_VITE_CAP_ARGS_JSON from the shell environment before a platform switch.

Example fix

# before
CAP_VITE_CAP_ARGS_JSON='["--target","abc"]' vite --config wrapper
# after
CAP_VITE_CAP_ARGS_JSON='["ios","--target","abc"]' vite --config wrapper
Defensive patterns

Strategy: validation

Validate before calling

function assertCapArgsEnv(raw: string | undefined): asserts raw is string | undefined {
  if (raw !== undefined) {
    const parsed = JSON.parse(raw)
    if (!Array.isArray(parsed) || !parsed.every(v => typeof v === 'string') || !['ios','android'].includes(parsed[0])) {
      throw new Error('CAP_VITE_CAP_ARGS_JSON must be a string[] starting with ios|android')
    }
  }
}

Type guard

function isValidCapArgsEnv(raw: string | undefined): boolean {
  if (!raw) return true
  try {
    const p = JSON.parse(raw)
    return Array.isArray(p) && p.every(v => typeof v === 'string') && (p[0] === 'ios' || p[0] === 'android')
  } catch { return false }
}

Try / catch

try {
  capVitePlugin({ capArgs })
} catch (error) {
  if (error instanceof Error && error.message.includes('ios` or `android')) {
    throw new Error('Plugin capArgs[0] must be ios|android. Check CAP_VITE_CAP_ARGS_JSON.')
  }
  throw error
}

Prevention

When it happens

Trigger: The wrapper Vite config parses CAP_VITE_CAP_ARGS_JSON into an array whose first element is not 'ios'/'android'; the env var was set manually with a wrong first token; capArgs is empty because the env var was unset (parseCapArgs returns [], so capArgs[0] is undefined -> parseCapacitorPlatform returns null).

Common situations: Running the wrapper config directly (vite --config <wrapper>) without the env var that runCapVite normally injects; a test harness that loads vite-wrapper-config.ts with a malformed CAP_VITE_CAP_ARGS_JSON; a stale env var from a previous different-platform run.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/72a2b9a166ca7bdf. Report an issue: GitHub.