Skip to content

AnthropicLlm sends a trailing assistant turn, which Claude 4.6+ rejects #7861

Closed

Description

@swf1996120

Found while testing LLM provider integrations in agent frameworks: an automated tool flags candidates and each one is reproduced before it is reported. Reproduced on 58332a2.

Describe the Bug:

When an agent runs again on its own last reply, AnthropicLlm sends a request whose last message is that assistant turn. A LoopAgent around a single Claude LlmAgent gets there on its second iteration: the request is [user, assistant].

Claude treats a final assistant message as a prefill. Claude 4.6 and later models don't support prefill and return a 400 ("This model does not support assistant message prefill. The conversation must end with a user message.", see Prefill not supported). Older models accept it but continue the old reply instead of answering again, and reject it if the text ends in whitespace.

The Gemini, LiteLlm and Apigee model classes avoid this by calling BaseLlm._maybe_append_user_content, which appends a short user turn when the history doesn't end with one. AnthropicLlm never calls it.

Steps to Reproduce:

  1. pip install google-adk anthropic (or a checkout at 58332a2)
  2. Run the snippet below. It gives AnthropicLlm an AsyncAnthropic client on a mock transport and prints the roles of each request's messages.

Expected Behavior:

Every request ends with a user message, as with the other model classes.

Observed Behavior:

['user']
['user', 'assistant']

I did not send these to a live endpoint.

Environment Details:

  • ADK Library Version (pip show google-adk): 2.13.0 (main at 58332a2)
  • Desktop OS: Linux
  • Python Version (python -V): 3.12.4

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used: AnthropicLlm(model="claude-sonnet-4-6")

Additional Context:

AnthropicLlm.generate_content_async converts llm_request.contents as they are, with nothing that makes the list end on a user turn:

messages = [
message
for message in (
_content_to_message_param(content, sanitizer)
for content in llm_request.contents or []
)
if message["content"]
]

A fix is to append the same user turn _maybe_append_user_content adds ("Continue processing previous requests as instructed. ...") when the converted messages end with an assistant message. It should go after the empty-message filter, so a dropped turn can't leave two user turns in a row, and after the cache breakpoint is placed, since the synthetic turn isn't part of the session history.

Minimal Reproduction Code:

import asyncio, json
try:
  import httpx2 as httpx  # newer anthropic SDKs ship their own httpx fork
except ImportError:
  import httpx
from anthropic import AsyncAnthropic
from google.adk.agents import LlmAgent, LoopAgent
from google.adk.models.anthropic_llm import AnthropicLlm
from google.adk.runners import InMemoryRunner

def handler(request):
  body = json.loads(request.content)
  print([m['role'] for m in body['messages']])
  return httpx.Response(200, json={
      'id': 'msg_1', 'type': 'message', 'role': 'assistant', 'model': body['model'],
      'content': [{'type': 'text', 'text': 'An old silent pond \n'}],
      'stop_reason': 'end_turn', 'usage': {'input_tokens': 10, 'output_tokens': 5}})

client = AsyncAnthropic(api_key='test', http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler)))
writer = LlmAgent(name='writer', model=AnthropicLlm(model='claude-sonnet-4-6', client=client),
                  instruction='Write or improve the haiku.')
runner = InMemoryRunner(agent=LoopAgent(name='refine', sub_agents=[writer], max_iterations=2))
asyncio.run(runner.run_debug('Write a haiku'))

How often has this issue occurred?:

  • Always (100%)

Activity

  1. added
    models[Component] This issue is related to model support
    on Oct 30, 2026
  2. sanketpatil06 commented on Nov 3, 2026

    @sanketpatil06

    Hi @swf1996120 , thanks for reporting this issue. we were able to reproduce this on our end. we see the PR #7862 is already opened to fix this issue, could you please try it out and let us know if it resolves the issue at your end before we move forward with review and merge?

  3. added
    request clarification[Status] The maintainer need clarification or more information from the author
    on Nov 3, 2026
  4. swf1996120 commented on Nov 3, 2026

    @swf1996120
    Author

    Thanks for checking. I ran the repro from this issue against #7862 (head c25bc40), still on the mocked transport, no live endpoint.

    With #7862 the second request is [user, assistant, user], the last turn being the "Continue processing previous requests..." text, so every request ends on a user turn. Without it the second request still ends on the assistant turn. So it resolves it on my side.

  5. removed
    request clarification[Status] The maintainer need clarification or more information from the author
    on Nov 3, 2026
  6. added a commit that references this issue on Nov 5, 2026
    92b93c8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

models[Component] This issue is related to model support

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions