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 runCapVite() when capArgs[0] is not parseable by parseCapacitorPlatform(), which only accepts the literal strings 'ios' or 'android'. This is the CLI entry point of @proj-airi/cap-vite and is the first guard before forwarding args into Vite and the Capacitor run subprocess.

Source

Thrown at packages/cap-vite/src/index.ts:157

    index += parsedArg.consumedArgs
  }

  return {
    baseConfigFile,
    configLoader,
    projectRoot,
    viteArgs: forwardedViteArgs,
    wrapperConfigFile: resolveWrapperConfigFile(),
  }
}

export async function runCapVite(
  viteArgs: string[],
  capArgs: string[],
  options: RunCapViteOptions = {},
): Promise<Output> {
  if (!parseCapacitorPlatform(capArgs[0])) {
    throw new Error('The first `cap run` argument must be `ios` or `android`.')
  }

  const cwd = resolve(options.cwd ?? process.cwd())
  const prepared = prepareCapViteLaunch(viteArgs, cwd)

  return await x('vite', ['--config', prepared.wrapperConfigFile, ...prepared.viteArgs], {
    throwOnError: false,
    nodeOptions: {
      cwd,
      env: {
        CAP_VITE_BASE_CONFIG: prepared.baseConfigFile ?? '',
        CAP_VITE_CAP_ARGS_JSON: JSON.stringify(capArgs),
        CAP_VITE_CONFIG_LOADER: prepared.configLoader ?? '',
        CAP_VITE_ROOT: prepared.projectRoot,
      },
      stdio: 'inherit',
    },
  })

View on GitHub (pinned to 27111382b4)

Solutions

  1. Ensure the first element of the capArgs array is exactly 'ios' or 'android' (lowercase).
  2. If the platform comes from user input, normalize and validate it before calling runCapVite using parseCapacitorPlatform().
  3. Reorder args so the platform precedes any --target flag: runCapVite(viteArgs, ['ios', '--target', deviceId]).

Example fix

// before
await runCapVite(viteArgs, ['--target', deviceId])
// after
await runCapVite(viteArgs, ['ios', '--target', deviceId])
Defensive patterns

Strategy: validation

Validate before calling

import { parseCapacitorPlatform } from '@proj-airi/cap-vite'

function assertCapPlatform(capArgs: string[]): asserts capArgs is ['ios' | 'android', ...string[]] {
  if (!parseCapacitorPlatform(capArgs[0])) {
    throw new TypeError(`capArgs[0] must be 'ios' or 'android', got: ${String(capArgs[0])}`)
  }
}

assertCapPlatform(capArgs)
await runCapVite(viteArgs, capArgs)

Type guard

import { parseCapacitorPlatform } from '@proj-airi/cap-vite'

function isCapArgs(value: unknown): value is ['ios' | 'android', ...string[]] {
  return Array.isArray(value)
    && (value[0] === 'ios' || value[0] === 'android')
    && value.every(v => typeof v === 'string')
}

Try / catch

try {
  await runCapVite(viteArgs, capArgs)
} catch (error) {
  if (error instanceof Error && error.message.includes('must be `ios` or `android`')) {
    console.error('Usage: cap-vite <ios|android> [vite args]')
    process.exit(2)
  }
  throw error
}

Prevention

When it happens

Trigger: Calling runCapVite(viteArgs, capArgs) with capArgs[0] missing, undefined, empty string, or any value other than 'ios'/'android' (e.g. 'web', 'electron', '--target'). Also triggered by argument-order mistakes where the platform is not placed first.

Common situations: Custom scripts that build capArgs dynamically and forget the platform token; CLI wrappers that pass '--target <id>' before the platform; typos like 'iOS'/'Android' with different casing; copying a Vite-only invocation into a cap-vite entry without adding the platform.

Related errors


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