moeru-ai/airi · error · TypeError
Expected `cap run --list --json` to return a JSON array.
Error message
Expected `cap run --list --json` to return a JSON array.
What it means
Thrown by parseCapacitorTargetList() when `cap run <platform> --list --json` produces output that JSON.parses to something other than an array. The function is the internal parser for listCapacitorTargets(), which shells out to the Capacitor CLI expecting a JSON array of target descriptors.
Source
Thrown at packages/cap-vite/src/native.ts:98
['app', 'src', 'main', 'res', 'xml', 'config.xml'],
['app', 'capacitor.build.gradle'],
['capacitor-cordova-android-plugins'],
['capacitor.settings.gradle'],
],
}
export function parseCapacitorPlatform(value: string | undefined): CapacitorPlatform | null {
return value === 'android' || value === 'ios' ? value : null
}
export function hasCapacitorTargetArg(capArgs: string[]): boolean {
return capArgs.some((arg, index) => arg === '--target' || (index > 0 && arg.startsWith('--target=')))
}
function parseCapacitorTargetList(value: string): CapacitorTarget[] {
const parsed = JSON.parse(value)
if (!Array.isArray(parsed)) {
throw new TypeError('Expected `cap run --list --json` to return a JSON array.')
}
return parsed
.filter((target): target is CapacitorTarget => typeof target === 'object' && target !== null && typeof (target as CapacitorTarget).id === 'string')
}
async function listCapacitorTargets(platform: CapacitorPlatform): Promise<CapacitorTarget[]> {
const output = await x('cap', ['run', platform, '--list', '--json'])
return parseCapacitorTargetList(output.stdout)
}
/**
* Resolves Capacitor run arguments by applying target defaults.
*
* Use when:
* - `cap-vite` is about to run `cap run`
* - Callers want env-based device IDs before falling back to the first available targetView on GitHub (pinned to 27111382b4)
Solutions
- Run `npx cap run ios --list --json` (or android) manually and confirm it emits a JSON array to stdout.
- Upgrade or pin @capacitor/cli to a version known to support --list --json.
- Pass --target explicitly (or set CAPACITOR_DEVICE_ID_IOS / CAPACITOR_DEVICE_ID_ANDROID) to bypass the listTargets code path entirely.
- Ensure only the JSON payload goes to stdout; redirect any CLI banners/warnings to stderr.
Example fix
// before: relies on `cap run --list --json` shape await runCapVite(viteArgs, ['ios']) // after: bypass target discovery with an explicit device id process.env.CAPACITOR_DEVICE_ID_IOS = deviceId await runCapVite(viteArgs, ['ios'])
Defensive patterns
Strategy: validation
Validate before calling
// Validate shape before relying on listCapacitorTargets output
async function safeListTargets(platform: 'ios' | 'android') {
const out = (await import('tinyexec').then(m => m.x('cap', ['run', platform, '--list', '--json']))).stdout
let parsed: unknown
try { parsed = JSON.parse(out) } catch { throw new Error('cap --list output was not JSON') }
if (!Array.isArray(parsed)) throw new Error('cap --list did not return an array')
return parsed.filter((t): t is { id: string } => t && typeof t.id === 'string')
} Type guard
function isTargetList(value: unknown): value is { id: string }[] {
return Array.isArray(value) && value.every(t => t !== null && typeof t === 'object' && typeof (t as any).id === 'string')
} Try / catch
try {
await runCapVite(viteArgs, ['ios'])
} catch (error) {
if (error instanceof TypeError && error.message.includes('JSON array')) {
console.error('Capacitor CLI did not return a target list. Check `cap run ios --list --json`.')
}
} Prevention
- Prefer an explicit --target when device discovery is unreliable.
- Pin a @capacitor/cli version that supports --list --json in your environment.
- Smoke-test `cap run <platform> --list --json` in CI to catch CLI regressions.
When it happens
Trigger: listCapacitorTargets(platform) is called (transitively from resolveCapRunArgs when no --target and no CAPACITOR_DEVICE_ID env is set), and the `cap` CLI emits a JSON object, a JSON scalar, or non-JSON text such as a warning, deprecation notice, or an error message routed to stdout.
Common situations: Capacitor CLI version mismatch where --list --json shape changed or is unsupported; `cap` not installed so the shell returns an error string; a newer/older @capacitor/cli that prints a banner before the JSON; CI environments where the CLI prints auth or telemetry noise to stdout.
Related errors
- cap-vite [vite args...] -- <ios|android> [cap run args...]
- The first `cap run` argument must be `ios` or `android`.
- CAP_VITE_CAP_ARGS_JSON must be a JSON string array.
- AIRI host websocket bridge is unavailable
- Missing value for `${optionName}`.
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/1dcf1946debb361e.
Report an issue: GitHub.