Back to guides
Troubleshooting·September 23, 2026·6 min read

“This model does not support assistant message prefill” — which Claude models dropped prefill, and what replaces it

Your messages array ends with an assistant turn, and Claude 4.6 and later models read that as a prefill and refuse it. End the conversation on a user message, then move whatever the prefill was doing — forcing JSON, skipping a preamble, holding a persona, resuming a cut-off answer — to the replacement Anthropic documents for it.

Last reviewed on .

Your messages array ends with an assistant turn, and Claude 4.6 and later models read that as a prefill and refuse it. End the conversation on a user message, then move whatever the prefill was doing — forcing JSON, skipping a preamble, holding a persona, resuming a cut-off answer — to the replacement Anthropic documents for it.

The error

Anthropic API response (HTTP 400)
{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "This model does not support assistant message prefill. The conversation must end with a user message."
  },
  "request_id": "req_..."
}

Causes and fixes at a glance

CauseFix
The last entry in messages has role: "assistant"That is a prefill, and Claude 4.6 and later models reject it. End on a user message.
A framework left an empty assistant message lastDrop a trailing assistant message that has no content before you send.
You prefilled “{” to force JSONUse structured outputs (output_config.format) instead.
You prefilled to skip a preamble, keep a persona or resume a cut-off answerA system-prompt instruction, a role in the system prompt, or a user-turn continuation.
A memory manager, agent loop or handoff left an assistant turn lastNormalize the tail once, right before the request, instead of in every code path.

Which Claude models reject prefill (as of September 2026)

Anthropic's error reference is blunt: Claude 4.6 and later models do not support prefilling the last assistant message, and a request that does gets exactly this 400 (https://platform.claude.com/docs/en/api/errors#prefill-not-supported). By name, that is Claude Opus 4.6 and every later Opus, Claude Opus 5.5 included (https://platform.claude.com/docs/en/models/opus-5-5/migration-guide); Claude Sonnet 4.6 and Claude Sonnet 5 (https://platform.claude.com/docs/en/models/sonnet-5/migration-guide); and Claude Fable 5 and Fable 5.1 (https://platform.claude.com/docs/en/models/fable-5-1/migration-guide). Claude Haiku 4.5 still accepts a prefill, as do Claude Sonnet 4.5 and Claude Opus 4.5 — which is why this error tends to arrive with a model-ID change rather than a code change. Assistant messages earlier in the conversation, few-shot examples included, are not affected.

End on a user turn — including the one-line fix

Often nobody wrote a prefill on purpose: something in the stack put an assistant message last. Public reports trace it to a memory manager that appends one (Strands issue #1694), an agent loop that re-requests after a turn it wrongly thinks is unfinished (opencode issue #46415), back-to-back replies and agent handoffs (LiveKit issue #4907), and an empty assistant message left at the end (AutoGen PR #7931). If the trailing message is empty, drop it — that is the one line. If it holds text you want continued, do what Anthropic's migration guidance says for continuations: put the continuation in a user message that quotes where the answer stopped. One documented exception to leave alone: a pause_turn from server tools such as web search is continued by sending the paused assistant content back as-is (https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools).

end_on_user.py
from openai import OpenAI

client = OpenAI(base_url="https://api.kunavo.com/v1", api_key="sk-kn-...")

def end_on_user(messages: list[dict]) -> list[dict]:
    """Claude 4.6 and later reject a conversation whose last turn is the assistant's."""
    last = messages[-1] if messages else None
    if not last or last["role"] != "assistant" or last.get("tool_calls"):
        return messages               # tool_calls are owed tool results instead
    content = last.get("content")
    if not content or (isinstance(content, str) and not content.strip()):
        return messages[:-1]          # the one line: drop an empty tail
    tail = content[-200:] if isinstance(content, str) else "..."
    return messages + [{
        "role": "user",
        "content": f"Your previous response was interrupted and ended with {tail!r}. "
                   "Continue from where you left off.",
    }]

messages = [
    {"role": "user", "content": "Explain HTTP caching in three bullets."},
    {"role": "assistant", "content": ""},  # the empty tail a framework left behind
]

resp = client.chat.completions.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=end_on_user(messages),
)
print(resp.choices[0].message.content)

Replace what the prefill was doing

Anthropic's prompting guide maps each old use of prefill to a replacement (https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#migrating-away-from-prefilled-responses). Forcing JSON: structured outputs, which constrain the response to your schema and are available on Claude Haiku 4.5, Sonnet 4.5, Opus 4.5 and every model since (https://platform.claude.com/docs/en/build-with-claude/structured-outputs); for YAML or another format, the guide's advice is to ask for the structure and retry when it misses. Classification: a tool with an enum of your valid labels, or structured outputs. Skipping a preamble: a system-prompt instruction such as “Respond directly without preamble.” Holding a persona: set the role in the system prompt (https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), and put periodic reminders in the user turn instead of a prefilled assistant one. A cut-off answer: the user-turn continuation above. Forcing a tool call is not a drop-in substitute everywhere — Claude Fable 5.1 and Claude Opus 5.5 reject tool_choice any and tool with a 400 of their own (https://platform.claude.com/docs/en/api/errors#forced-tool-use-not-supported).

structured-output.sh
# Before: messages ended with {"role": "assistant", "content": "{"}
# Through Kunavo, parse and check the JSON that comes back (see below).
curl https://api.kunavo.com/v1/messages \
  -H "x-api-key: sk-kn-..." \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "messages": [
      {"role": "user", "content": "Extract the name and email: John Smith <john@example.com>"}
    ],
    "output_config": {
      "format": {
        "type": "json_schema",
        "schema": {
          "type": "object",
          "properties": {
            "name": {"type": "string"},
            "email": {"type": "string"}
          },
          "required": ["name", "email"],
          "additionalProperties": false
        }
      }
    }
  }'

If you’re calling through Kunavo

Kunavo forwards a trailing assistant turn; it does not repair it. /v1/messages hands your messages array to the upstream untouched, and on /v1/chat/completions the translator carries a final role: "assistant" message across as a final assistant turn in the Claude request — an empty one included, so drop empty ones yourself. Anthropic rejects a prefilled final turn on every claude-* model we serve except claude-haiku-4-5, and that 400 reaches you with the message text intact, followed by the upstream's request id: Anthropic-shaped on /v1/messages, where error.type reads api_error rather than invalid_request_error, and OpenAI-shaped on /v1/chat/completions (type upstream_error, code upstream_400). A 400 is never retried on another channel, and the failed call is recorded at zero cost. For JSON, the chat translator carries neither response_format nor output_config to Claude, while /v1/messages forwards output_config as you sent it; we have not verified that the schema is enforced end to end, so parse and check the JSON that comes back. The native endpoint, and the request shape it passes through, is documented in the Messages API docs.

FAQ

Which Claude models don't support assistant message prefill?

Per Anthropic's docs as of September 2026, every Claude model from 4.6 on: Claude Opus 4.6 and every later Opus, Claude Sonnet 4.6 and Sonnet 5, Claude Fable 5 and 5.1, and the Mythos models. Claude Haiku 4.5, Sonnet 4.5 and Opus 4.5 still accept one.

How do I force JSON output from Claude without prefill?

Use structured outputs: pass a JSON schema in output_config.format and the response is constrained to it. Anthropic lists message prefilling as incompatible with JSON outputs in any case.

Why do I get this error when I never prefilled anything?

Something in your stack left an assistant message last — a memory or session manager, an agent loop re-requesting after a turn, an agent handoff, or an empty assistant message nobody removed. Log the messages array exactly as it is sent; its last element will have role: "assistant".

Can I still include assistant messages in the conversation?

Yes. Only the final turn is restricted; earlier assistant messages, few-shot examples included, are unaffected.

Why does it happen through an OpenAI-compatible endpoint too?

Because the gateway carries your final role: "assistant" message across as a final Claude assistant turn — the same prefill, reshaped. Kunavo's chat translator does exactly that, and the same message has been reported through other OpenAI-compatible gateways (opencode issue #13768). The fix is the same: end on a user message.

Related guides

More error semantics live in the error reference; getting a key takes a minute via sign up and the authentication docs.