moeru-ai/airi · error · Error

Use invoke with a desktop command or --help.

Error message

Use invoke with a desktop command or --help.

What it means

run() validates the parsed argv against the small supported surface: it must start with 'invoke' followed by --help/-h/help or a command of the form app.*/display.*/screen.*/window.*/input.*/mediaControl.*. Anything else is rejected because the host only exposes a curated subset of the computer-use CLI.

Solutions

  1. Use argv = ['invoke', '<command>'] where command matches app.*/display.*/screen.*/window.*/input.*/mediaControl.* or --help/-h/help
  2. Check the exposed command list in the runtime/CLI help before constructing argv
  3. Validate the LLM-generated command against the regex before calling run

Example fix

// before
await run({ argv: ['devices', 'list'] })
// after
await run({ argv: ['invoke', 'app.list'] })
Defensive patterns

Strategy: validation

Validate before calling

const COMMAND_RE = /^(?:--help|-h|help|(?:app|display|screen|window|input|mediaControl)\.[a-zA-Z]+)$/nfunction isValidInvoke(argv: unknown[]): boolean {
  return argv[0] === 'invoke' && COMMAND_RE.test(String(argv[1]))
}

Type guard

function isComputerUseArgv(v: unknown): v is ['invoke', string, ...string[]] {
  return Array.isArray(v) && v[0] === 'invoke' && typeof v[1] === 'string'
    && /^(?:--help|-h|help|(?:app|display|screen|window|input|mediaControl)\.[a-zA-Z]+)$/.test(v[1])
}

Try / catch

try {
  return await run({ argv })
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Use invoke with a desktop command')) {
    console.warn(`Unsupported command: ${argv.join(' ')}; see help for allowed commands`)
    return helpResult
  }
  throw e
}

Prevention

When it happens

Trigger: Passing argv[0] other than 'invoke', an unknown command namespace (e.g. 'browser.open'), or a bare subcommand name not matching the regex as argv[1].

Common situations: Copy-pasting CLI examples from upstream docs that use subcommands AIRI does not expose; typos like 'invok'; passing empty or malformed argv from an LLM tool call.

Understand the failure class

Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.

Related errors


AI-assisted analysis of moeru-ai/airi@438a067dde (2026-09-17). Data as JSON: /api/errors/bbc475cabc93362b. Report an issue: GitHub.

Appendix: source

Thrown at apps/stage-tamagotchi/src/main/services/airi/computer-use/runtime.ts:88

      socketDirectory = undefined
    }
  }

  function enqueue<T>(operation: () => Promise<T>): Promise<T> {
    const next = queue.then(() => {
      if (disposed)
        throw new Error('Computer use has stopped.')
      return operation()
    })
    // A failed command must not prevent later commands from running.
    queue = next.then(() => undefined, () => undefined)
    return next
  }

  async function run(input: unknown): Promise<ComputerUseResult> {
    const { argv } = v.parse(inputSchema, input)
    if (argv[0] !== 'invoke' || !/^(?:--help|-h|help|(?:app|display|screen|window|input|mediaControl)\.[a-zA-Z]+)$/.test(argv[1]))
      throw new Error('Use invoke with a desktop command or --help.')

    // The host owns routing and artifact storage. CLI global flags must not replace them.
    const managedFlags = ['--store-root', '--endpoint', '--device', '--device-id', '--run', '--config', '--profile', '--discovery-file', '--pairing-store']
    if (argv.some(arg => managedFlags.some(flag => arg === flag || arg.startsWith(`${flag}=`))))
      throw new Error('Computer use routing and storage are managed by AIRI.')

    return enqueue(async () => {
      const active = await connect()
      const command = [...argv]
      if (!command.includes('--json') && !command.includes('--help') && !command.includes('-h') && command[1] !== 'help')
        command.push('--json')
      command.push('--store-root', options.storeRoot)
      let stdout: string
      let stderr: string
      let exitCode = 0
      try {
        ({ stdout, stderr } = await execFileAsync(options.binaryPath, command, {
          env: { ...process.env, AUV_ENDPOINT: active.connectionOptions.endpoint, AUV_CONTEXT: undefined },

View on GitHub (pinned to 438a067dde)