zed-industries/zed · error

The agent reached the maximum number of tokens.

Error message

The agent reached the maximum number of tokens.

What it means

The Zed agent's subagent runner maps the ACP (Agent Client Protocol) StopReason::MaxTokens to this error: the model stopped mid-task because it exhausted the per-run token budget before producing a usable final answer. It is surfaced to the parent thread as an anyhow error, optionally with the subagent's partial output appended.

Solutions

  1. Break the task into smaller prompts and run the subagent multiple times
  2. Enable context auto-compaction so the thread is compacted before limits are hit
  3. Reduce inputs: read fewer/smaller files or trim the prompt given to the subagent
  4. Switch to a model with a larger context window
  5. Prompt the thread again to let the agent wrap up or hand off its partial work

Example fix

// before: one oversized subagent prompt
thread.send("Rewrite all files in crates/ui in one go");

// after: scoped, smaller task
thread.send("Rewrite only crates/ui/src/button.rs, summarizing the rest");
Defensive patterns

Strategy: try-catch

Validate before calling

// before prompting, check token headroom
let usage = thread.read(cx).latest_token_usage();
if usage.map_or(false, |u| u.ratio() > 0.8) {
    // compact or start a new thread before sending a large task
}

Try / catch

match subagent_result {
    Err(e) if e.to_string().contains("maximum number of tokens") => {
        // split the task and retry with a smaller scope
    }
    other => other?,
}

Prevention

When it happens

Trigger: A subagent prompt requires so many model requests/tokens (large tool outputs, huge file reads, long generations) that the backend reports stop_reason=MaxTokens instead of EndTurn. Occurs in Agent::run via the acp_thread send/response loop.

Common situations: Asking the subagent to summarize or rewrite a very large codebase section in one turn; runaway tool-call loops consuming the context window; model with small context limit handling an oversized prompt.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of zed-industries/zed@916fc2b8cb (2026-09-19). Data as JSON: /api/errors/494959e4436ced30. Report an issue: GitHub.

Appendix: source

Thrown at crates/agent/src/agent.rs:3536

                    } else {
                        thread.update(cx, |thread, cx| thread.cancel(cx)).await;
                        Err(anyhow!(
                            "The agent is nearing the end of its context window and has been \
                             stopped. You can prompt the thread again to have the agent wrap up \
                             or hand off its work."
                        ))
                    }
                }
            };
            let discard_partial_output = matches!(
                &response,
                Ok(Some(response)) if response.stop_reason == acp::StopReason::Cancelled
                    || response.stop_reason == acp::StopReason::Refusal
            );
            let result = match response {
                Ok(Some(response)) => match response.stop_reason {
                    acp::StopReason::Cancelled => Err(anyhow!("User canceled")),
                    acp::StopReason::MaxTokens => Err(anyhow!("The agent reached the maximum number of tokens.")),
                    acp::StopReason::MaxTurnRequests => Err(anyhow!("The agent reached the maximum number of allowed requests between user turns. Try prompting again.")),
                    acp::StopReason::Refusal => Err(anyhow!("The agent refused to process that prompt. Try again.")),
                    _ => thread.read_with(cx, |thread, _cx| {
                        thread
                            .last_message()
                            .and_then(|message| {
                                let content = message.as_agent_message()?
                                    .content
                                    .iter()
                                    .filter_map(|content| match content {
                                        AgentMessageContent::Text(text) => Some(text.as_str()),
                                        _ => None,
                                    })
                                    .join("\n\n");
                                if content.is_empty() {
                                    None
                                } else {
                                    Some(content)

View on GitHub (pinned to 916fc2b8cb)