Skip to content

[BUG] A turn cut short by max_tokens is reported as a completed task #2895

Description

@fiunchinho

🎯 Affected Service(s)

App Service (the Go ADK runtime, go/adk/pkg/a2a)

🚦 Impact/Severity

Blocker for any client that has to trust the answer: the truncation is silent and cannot be detected downstream.

🐛 Bug Description

When a model stops because it reached its output limit, the A2A task still ends in TASK_STATE_COMPLETED, and the answer the client receives is cut mid-sentence with no indication that anything is missing.

The Anthropic adapter already carries the signal. anthropicStopReasonToGenai in go/adk/pkg/models/anthropic_adk.go maps stop_reason: max_tokens to genai.FinishReasonMaxTokens on the model.LLMResponse, but nothing downstream acts on it: the ADK A2A executor finishes the task as completed, and the finish reason never reaches the A2A surface. The OpenAI chat and Responses adapters and the Ollama adapter map their own "length" reasons to the same value, so this is not Anthropic-specific.

Concretely, with an Anthropic ModelConfig that does not set maxTokens, the adapter sends its default max_tokens: 8192. Claude Sonnet 5 runs adaptive thinking by default and thinking tokens count against max_tokens, so a long agentic turn can spend most of the budget before it starts writing the answer. On a turn with roughly 190k input tokens and 20 tool calls, the first visible token appeared after 76 s and the response came back with output_tokens: 8192 against max_tokens: 8192 and stop_reason: max_tokens — the answer stopped in the middle of a sentence.

Nothing after that point can tell the difference. The A2A clients — a chat front end showing the agent's reply, and a parent agent calling this one through the remote A2A agent tool — see a completed task with a text artifact, so a parent agent will summarise or act on a half answer as if it were the whole one.

🔄 Steps To Reproduce

  1. Create an Anthropic ModelConfig for claude-sonnet-5 (or any thinking-capable model) and leave maxTokens unset, so the adapter default of 8192 applies.
  2. Point an Agent at it and give it tools, so a turn accumulates a large context.
  3. Ask a question whose answer is long — a report over the tool output, for example — on a thread that has already run many tool calls.
  4. Watch the turn over A2A (a client, or kagent invoke).
  5. The answer stops mid-sentence, and the task ends TASK_STATE_COMPLETED. The Anthropic response for that call shows stop_reason: max_tokens with output_tokens equal to the request's max_tokens.

🤔 Expected Behavior

The task reports the truncation. A turn whose final model response ends with genai.FinishReasonMaxTokens should not be reported as completed: the task should end failed, with a status message naming what happened and the limit, while the text already streamed is kept so the caller still has the partial answer.

📱 Actual Behavior

The task ends TASK_STATE_COMPLETED with the cut text as its only artifact, and no status message. The finish reason is dropped at the executor boundary.

💻 Environment

  • kagent go/adk A2A executor (go/adk/pkg/a2a/executor.go), ADK google.golang.org/adk/v2 v2.3.0
  • Anthropic adapter, claude-sonnet-5, ModelConfig without maxTokens
  • Reproducible on any provider whose adapter maps its stop reason to genai.FinishReasonMaxTokens

🔍 Additional Context

Raising maxTokens on the ModelConfig makes the truncation rarer but does not fix it: any cap can be reached, and with thinking models the share of the budget that reaches the answer is not predictable. The missing piece is that the outcome is not reported, not that the default is low.

I have a fix ready and will open a PR against this issue: the turn's last model response is watched for genai.FinishReasonMaxTokens and the terminal task status is rewritten from completed to failed with a message naming the limit, leaving the streamed artifacts in place.

🙋 Are you willing to contribute?

  • I am willing to submit a PR to fix this issue

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions