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
To auto-select a device, cap-vite runs `cap run <platform> --list --json` and expects stdout to be a JSON array; parseCapacitorTargetList throws this TypeError when JSON.parse succeeded but the top-level value is not an array (e.g. an object payload). Note that non-JSON stdout instead surfaces as a SyntaxError from JSON.parse itself.
Solutions
- Run `npx cap run ios --list --json` yourself and inspect the actual stdout shape
- Align the project's @capacitor/cli version with the version cap-vite depends on (or update cap-vite)
- Ensure the project-local cap binary is the one resolved (npx from the project root), not a stale global install
- If output shape legitimately changed, report it upstream to cap-vite with the captured JSON
Example fix
# before
# trust `cap run ios --list --json` output shape blindly
# after
$ npx cap run ios --list --json # inspect stdout; it must be a JSON array like [{"id":"...","name":"..."}]
$ pnpm update @capacitor/cli # align versions if the shape differs Defensive patterns
Strategy: try-catch
Validate before calling
// Sanity-check the CLI output shape before trusting it
const output = await x('cap', ['run', platform, '--list', '--json'])
const trimmed = output.stdout.trim()
if (!trimmed.startsWith('['))
throw new Error(`cap --list did not emit a JSON array: ${trimmed.slice(0, 200)}`) Type guard
function isCapacitorTargetList(v: unknown): v is Array<{ id: string }> {
return Array.isArray(v) && v.every(t => typeof t === 'object' && t !== null && typeof (t as { id?: unknown }).id === 'string')
} Try / catch
try {
const targets = await listCapacitorTargets(platform)
}
catch (err) {
if (err instanceof TypeError || err instanceof SyntaxError) {
// inspect actual `npx cap run <platform> --list --json` output; likely a CLI version mismatch
logCapListOutput(platform)
}
throw err
} Prevention
- Pin the @capacitor/cli version in the project so output shape is deterministic
- Run `npx cap run <platform> --list --json` after any Capacitor upgrade
- Keep CLI diagnostics on stderr so stdout stays parseable
When it happens
Trigger: An @capacitor/cli version whose --list --json emits an object or differently shaped payload; stdout polluted by extra log output that still parses as JSON but not as an array; a wrapper `cap` binary printing its own structure.
Common situations: Version drift between the locally installed @capacitor/cli and what cap-vite was built against; a global `cap` shadowing the project one via npx/PATH; plugin logs routed to stdout in CI.
Related errors
- CAP_VITE_CAP_ARGS_JSON must be a JSON string array.
- cap-vite [vite args...] -- <ios|android> [cap run args...]
- Extension manifest is not valid JSON
- The file does not contain valid JSON.
- Beat Sync is not available in Stage Pocket
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/1dcf1946debb361e.
Report an issue: GitHub.
Appendix: 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 677329427f)