moeru-ai/airi · error · Error

skip() cannot be mixed with other tool calls in the same scr

Error message

skip() cannot be mixed with other tool calls in the same script

What it means

Thrown by JavaScriptPlanner.runAction() when this.activeRun.sawSkip is already true and a subsequent non-skip tool is invoked. skip() is a terminal 'do nothing this turn' sentinel; mixing it with real actions is contradictory, so the planner rejects it. sawSkip is set the moment skip runs, so any later tool call (in the same script, even async) trips this guard.

Source

Thrown at integrations/minecraft/src/cognitive/conscious/js-planner.ts:1405

        return { [keys[0]]: firstArg }
    }

    const params: Record<string, unknown> = {}
    for (const [index, key] of keys.entries()) {
      if (index >= args.length)
        break
      params[key] = args[index]
    }

    return params
  }

  private async runAction(tool: string, params: Record<string, unknown>): Promise<ActionRuntimeResult> {
    if (!this.activeRun)
      throw new Error('Tool calls are only allowed during REPL evaluation')

    if (this.activeRun.sawSkip && tool !== 'skip')
      throw new Error('skip() cannot be mixed with other tool calls in the same script')

    if (this.activeRun.actionCount >= this.maxActionsPerTurn)
      throw new Error(`Action limit exceeded: max ${this.maxActionsPerTurn} actions per turn`)

    if (tool === 'skip')
      this.activeRun.sawSkip = true

    this.activeRun.actionCount++

    if (tool === 'skip') {
      const action: ActionInstruction = { tool: 'skip', params: {} }
      const runtimeResult: ActionRuntimeResult = {
        action,
        ok: true,
        result: 'Skipped turn',
      }
      this.activeRun.executed.push(runtimeResult)
      return runtimeResult

View on GitHub (pinned to 27111382b4)

Solutions

  1. Treat skip() as strictly terminal: never call another tool after it in the same script.
  2. Branch with if/else so only one of skip or the real action runs.
  3. If you want to do nothing, call skip() alone and return.

Example fix

// before
//   if (tired) { skip() }
//   await use('mineBlockAt', { position: p })  // throws if skip ran
//
// after
//   if (tired) { skip(); return }
//   await use('mineBlockAt', { position: p })
Defensive patterns

Strategy: validation

Validate before calling

// Treat skip() as terminal and never call another tool afterwards:
// if (shouldSkip) { skip(); return }
// await use('moveTo', target)  // only reached when not skipping

Prevention

When it happens

Trigger: A script calls skip() and then later calls use('moveTo', ...) or any non-skip tool; an async flow where skip() resolves first and a pending action call follows; the model uses skip as a 'default' then overrides it with a real action.

Common situations: Model writes conditional logic with skip in one branch and a real action in another, both reachable; skip used as a fallback after an early return that did not actually return; retry logic that calls skip then re-attempts a real tool.

Related errors


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