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 = falseView on GitHub (pinned to 27111382b4)
Solutions
- Do not invoke the wrapper config directly; go through runCapVite() which sets CAP_VITE_CAP_ARGS_JSON correctly.
- If you must set it manually, ensure CAP_VITE_CAP_ARGS_JSON is a JSON array like ["ios"] or ["android","--target","<id>"].
- 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
- Always go through runCapVite() so the env var is set correctly.
- Never reuse CAP_VITE_CAP_ARGS_JSON across platform switches without clearing it.
- Unit-test the wrapper config with a known-good env value.
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
- CAP_VITE_CAP_ARGS_JSON must be a JSON string array.
- The first `cap run` argument must be `ios` or `android`.
- AIRI host websocket bridge is unavailable
- cap-vite [vite args...] -- <ios|android> [cap run args...]
- Expected `cap run --list --json` to return a JSON array.
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/72a2b9a166ca7bdf.
Report an issue: GitHub.