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 runtimeResultView on GitHub (pinned to 27111382b4)
Solutions
- Treat skip() as strictly terminal: never call another tool after it in the same script.
- Branch with if/else so only one of skip or the real action runs.
- 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
- Never call a non-skip tool after skip() in the same script.
- Use if/else branching so only one of skip or the action is reachable.
- Return immediately after skip() to make the terminal intent explicit.
- Avoid retry loops that call skip() and then re-attempt a real action.
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
- Unknown tool: ${tool}
- Action limit exceeded: max ${this.maxActionsPerTurn} actions
- Your last reply was natural language, not JavaScript, so not
- Expectation failed: Condition evaluated to false
- Expectation failed: Expected movedDistance >= ${threshold},
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/674b9029c2ed9ca3.
Report an issue: GitHub.