abi/screenshot-to-code · error · Exception

Agent exceeded max tool turns

Error message

Agent exceeded max tool turns

What it means

Generic Exception raised at the end of _run_with_session when the agent exhausts its hardcoded 30-step tool loop (for _ in range(max_steps) with max_steps = 30) without the model ever producing a tool-call-free final turn. Each iteration is one model turn; the loop only exits early when turn.tool_calls is empty (final answer).

Source

Thrown at backend/agent/engine.py:327

                            len(tool_result.updated_content), "tool_result"
                        )

                await self._send(
                    "toolResult",
                    data={
                        "name": tool_call.name,
                        "output": tool_result.summary,
                        "ok": tool_result.ok,
                    },
                    event_id=tool_event_id,
                )
                executed_tool_calls.append(
                    ExecutedToolCall(tool_call=tool_call, result=tool_result)
                )

            await session.append_tool_results(turn, executed_tool_calls)

        raise Exception("Agent exceeded max tool turns")

    async def run(self, model: Llm, prompt_messages: List[ChatCompletionMessageParam]) -> str:
        self.tool_runtime.input_images = self._extract_input_images(prompt_messages)
        seed_file_state_from_messages(self.file_state, prompt_messages)

        if self.recorder is not None:
            self.recorder.record_run_start(model, prompt_messages)

        session = create_provider_session(
            model=model,
            prompt_messages=prompt_messages,
            should_generate_images=self.should_generate_images,
            openai_api_key=self.openai_api_key,
            openai_base_url=self.openai_base_url,
            anthropic_api_key=self.anthropic_api_key,
            gemini_api_key=self.gemini_api_key,
            replicate_api_key=self.replicate_api_key,
            # Only advertise extraction when the request actually contains a

View on GitHub (pinned to d026163f58)

Solutions

  1. Use a stronger model that converges in fewer turns.
  2. Tighten the user prompt so the model knows to output final code and stop calling tools.
  3. Raise max_steps in backend/agent/engine.py:219 if the workload legitimately needs more turns.
  4. Check the run recording (recorder logs every turn) to see which tool the model loops on and fix that tool's result so the model can proceed.

Example fix

# before (backend/agent/engine.py)
max_steps = 30

# after
max_steps = 50  # still bounded, but allows larger tasks
Defensive patterns

Strategy: try-catch

Try / catch

try:
    html = await engine.run(model, messages)
except Exception as e:
    if "exceeded max tool turns" in str(e):
        # retry with stronger model or simplified prompt
        ...

Prevention

When it happens

Trigger: The model keeps requesting tool calls (editing files, generating images, reading state) for 30 consecutive turns and never emits a plain text final response.

Common situations: Under-powered models stuck in a tool loop, prompts that never tell the model when to stop, or a task too large for 30 turns (big multi-file generation with image assets).

Related errors


AI-assisted analysis of abi/screenshot-to-code@d026163f58 (2026-08-14). Data as JSON: /api/errors/be99c43d10188f15. Report an issue: GitHub.