Claude Prefill Deprecated: Fix Your JSON Scaffolding (Sonnet 4.6 / Opus 4.6)
Prefill removed in Claude Sonnet 4.6, Opus 4.6, and Opus 4.7. If your JSON scaffolding is throwing 400 errors or returning broken output, here's the migration path using output_config.format with full code examples.
Have broken JSON right now? Fix it free in under 1 second — no signup.
Fix My JSON →If you've been using Claude's prefill feature to force JSON output — putting {" or { as the first message in the assistant turn — it stopped working. Anthropic deprecated prefill in Claude Sonnet 4.6 and Claude Opus 4.6 (and every subsequent model). Any code relying on that pattern now fails with a 400 error or returns unexpected output.
This guide explains exactly what changed, which models are affected, why Anthropic removed it, and the migration path to guaranteed structured output that works today.
What Prefill Was (and Why Developers Used It)
Prefill let you pre-load the beginning of Claude's response by appending an assistant message before sending the API request. Developers used it almost universally for one thing: forcing JSON output without needing tool use or a schema.
# OLD PATTERN — broken on Sonnet 4.6+
messages = [
{"role": "user", "content": "Extract the name and age from: John is 30 years old."},
{"role": "assistant", "content": "{"} # <-- prefill hack
]
Claude would continue from where you left off and output valid-looking JSON. It wasn't guaranteed — Claude could still go off-script — but it worked often enough that it became the default approach in many LLM codebases.
The pattern had variations:
# Variant 1: prefill with opening brace
{"role": "assistant", "content": "{"}
Variant 2: prefill with key to lock in field name
{"role": "assistant", "content": '{"name":'}
Variant 3: prefill full structure for very specific output
{"role": "assistant", "content": '{"items": ['}
All of these are now invalid on new Claude models.
Which Models Removed Prefill
Prefill is no longer supported in:
| Model | Prefill status |
|---|---|
| Claude Sonnet 4.6 | Removed |
| Claude Opus 4.6 | Removed |
| Claude Opus 4.7 | Removed |
| Claude Opus 4.8 | Removed |
| Claude Mythos Preview | Removed |
Prefill still works on older Claude 3 models (Haiku 3, Sonnet 3.5, Opus 3). If you're on those models and haven't migrated yet, note that they'll eventually be deprecated too — the right time to migrate is now.
What the Error Looks Like
Depending on your SDK version and how you structured the prefill, you'll see one of these:
400 Bad Request:{"type":"error","error":{"type":"invalid_request_error","message":"messages: content field must not contain an assistant turn prefill for this model"}}
Silent failure (older SDK): The request completes but Claude ignores the prefill constraint and outputs free text instead of JSON. This is worse than a 400 error because it fails silently.
400 with streaming:
BadRequestError: 400 {"type":"error","error":{"type":"invalid_request_error",
"message":"Prefill is not supported for claude-sonnet-4-6"}}
Why Anthropic Removed It
Prefill had three fundamental problems:
1. It couldn't guarantee JSON. The prefill nudged Claude toward JSON but didn't constrain the token generation. Models could still output prose, append extra text after the closing brace, or produce structurally valid JSON that didn't match your expected schema. 2. It conflicted with extended thinking. Claude's chain-of-thought reasoning (extended thinking mode) requires the full assistant turn to be generated, not pre-seeded. Prefill and thinking are architecturally incompatible. 3. It prevented proper structured output enforcement. Withoutput_config.format, Anthropic can validate output against a JSON Schema at generation time using constrained decoding — masking tokens that would produce invalid output. You can't do that when you've already written the first token.
The replacement is strictly better: guaranteed schema compliance with zero post-processing needed.
The Migration: output_config.format
The current way to get guaranteed JSON from Claude is the output_config.format parameter, available in Claude Sonnet 4.6 and later.
Python Migration
Before (broken on Sonnet 4.6+):import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Extract name and age from: John is 30 years old."
},
{
"role": "assistant",
"content": "{" # This now throws 400
}
]
)
print(response.content[0].text)
After (works on Sonnet 4.6+):
import anthropic
import json
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
output_config={
"format": {
"type": "json_schema",
"json_schema": {
"name": "person_extraction",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"]
}
}
}
},
messages=[
{
"role": "user",
"content": "Extract name and age from: John is 30 years old."
}
]
)
output_config.format guarantees valid JSON — safe to parse directly
data = json.loads(response.content[0].text)
print(data) # {"name": "John", "age": 30}
The key difference: the output is now validated against the schema during generation. If Claude would produce output that violates the schema, it's masked at the token level — the model physically cannot produce non-compliant output.
TypeScript/Node.js Migration
Before (broken on Sonnet 4.6+):import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [
{
role: "user",
content: "Extract name and age from: John is 30 years old.",
},
{
role: "assistant",
content: "{", // throws 400 on Sonnet 4.6+
},
],
});
After (works on Sonnet 4.6+):
import Anthropic from "@anthropic-ai/sdk";
import { z } from "zod";
const client = new Anthropic();
const PersonSchema = z.object({
name: z.string(),
age: z.number().int(),
});
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
output_config: {
format: {
type: "json_schema",
json_schema: {
name: "person_extraction",
schema: {
type: "object",
properties: {
name: { type: "string" },
age: { type: "integer" },
},
required: ["name", "age"],
},
},
},
},
messages: [
{
role: "user",
content: "Extract name and age from: John is 30 years old.",
},
],
});
// Safe to parse — schema validated at generation time
const raw = response.content[0].type === "text" ? response.content[0].text : "";
const data = PersonSchema.parse(JSON.parse(raw));
console.log(data); // { name: "John", age: 30 }
Pairing output_config.format with Zod validation gives you double coverage: the API guarantees schema-valid JSON, and Zod catches any edge cases at the TypeScript type level.
Using Tool Use as an Alternative
If output_config.format isn't available in your SDK version yet, the tool use pattern is the second-best option and works on all recent Claude models:
import anthropic
import json
client = anthropic.Anthropic()
tools = [
{
"name": "extract_person",
"description": "Extract person information from text",
"input_schema": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "Person's full name"},
"age": {"type": "integer", "description": "Person's age in years"}
},
"required": ["name", "age"]
}
}
]
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
tool_choice={"type": "tool", "name": "extract_person"},
messages=[
{
"role": "user",
"content": "Extract name and age from: John is 30 years old."
}
]
)
Tool use returns structured input, not a text block
tool_use = next(b for b in response.content if b.type == "tool_use")
data = tool_use.input # already a dict, no JSON.parse needed
print(data) # {"name": "John", "age": 30}
tool_choice: {"type": "tool", "name": "..."} forces Claude to call the specified tool, which means the response is always the input dict — never free text.
Supporting Both Old and New Models
If you have a codebase that needs to support both Claude 3.x (where prefill works) and Claude 4.x (where it doesn't), the cleanest approach is a capability flag:
import anthropic
import json
PREFILL_UNSUPPORTED_MODELS = {
"claude-sonnet-4-6",
"claude-opus-4-6",
"claude-opus-4-7",
"claude-opus-4-8",
}
def extract_json(client, model: str, prompt: str, schema: dict) -> dict:
if model in PREFILL_UNSUPPORTED_MODELS:
# New path: output_config.format
response = client.messages.create(
model=model,
max_tokens=1024,
output_config={"format": {"type": "json_schema", "json_schema": {"name": "output", "schema": schema}}},
messages=[{"role": "user", "content": prompt}]
)
return json.loads(response.content[0].text)
else:
# Legacy path: prefill (Claude 3.x only)
response = client.messages.create(
model=model,
max_tokens=1024,
messages=[
{"role": "user", "content": prompt},
{"role": "assistant", "content": "{"}
]
)
return json.loads(response.content[0].text)
As you complete migration, you can remove the legacy branch entirely and simplify to the new pattern.
Validating and Repairing Output During Migration
During a migration window, you may have mixed model usage where some requests go to Claude 3.x (prefill) and others to 4.x (no prefill). If any pre-structured output still comes back malformed — particularly if you're using the tool use path and post-processing the input dict — AI JSONMedic's repair endpoint handles all Claude-specific error patterns (markdown fences, Python booleans, truncation) as a fallback layer.
The full taxonomy of Claude JSON failure modes is covered in the LLM JSON Repair Guide. The fix invalid JSON guide covers the 14 most common patterns from any LLM.
Pydantic Integration (Python)
If you're using Instructor to wrap the Anthropic client, the migration is handled for you — Instructor uses tool use internally and doesn't rely on prefill:
import instructor
import anthropic
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
client = instructor.from_anthropic(anthropic.Anthropic())
Instructor handles everything — no prefill, no output_config needed
person = client.chat.completions.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[
{"role": "user", "content": "Extract name and age from: John is 30 years old."}
],
response_model=Person,
)
print(person) # name='John' age=30
Instructor's from_anthropic wrapper translates the Pydantic model to a tool definition automatically, so your migration is just replacing the direct Anthropic client with an Instructor-wrapped one.
Common Mistakes After Migration
Mistake 1: Mixingoutput_config with prefill on the same request.
You can't use both. Remove the assistant prefill message entirely when using output_config.format.
json_object type instead of json_schema.
{"type": "json_object"} tells Claude to output any valid JSON. It doesn't validate against your schema. Use {"type": "json_schema", "json_schema": {...}} for guaranteed structure.
Mistake 3: Forgetting to update the required array.
The JSON Schema required field must list every property you need. Missing it means Claude might omit fields silently.
output_config requires anthropic SDK ≥ 0.40.0 (Python) and @anthropic-ai/sdk ≥ 0.36.0 (Node.js). Pin your dependency if you're hitting TypeError: unexpected keyword argument.
Summary
Prefill is gone from Claude Sonnet 4.6 and all newer models. The migration path is output_config.format with a JSON Schema, which gives you better guarantees than prefill ever did — validated at token generation time, not guessed at in post-processing. Use tool use as a fallback if your SDK hasn't shipped output_config support yet. Pair either approach with Pydantic (Python) or Zod (TypeScript) for type-safe handling downstream.
If you're mid-migration and getting malformed output during the transition, paste it into AI JSONMedic for instant repair with an explanation of exactly what broke.
FAQ
What is Claude prefill and why was it removed?
Prefill let developers seed the beginning of Claude's response by adding an assistant message before sending the request. It was commonly used to force JSON output (e.g., prefilling with {). Anthropic removed it in Sonnet 4.6 and Opus 4.6 because it conflicted with extended thinking mode, couldn't guarantee schema compliance, and was replaced by the superior output_config.format parameter.
Which Claude models still support prefill?
Claude 3 models (Haiku 3, Sonnet 3.5, Opus 3) still support prefill. All Claude 4 series models — Sonnet 4.6, Opus 4.6, Opus 4.7, Opus 4.8, and Mythos Preview — have removed prefill support.
What error do I get when using prefill on Claude Sonnet 4.6?
You'll typically get a 400 Bad Request error: "Prefill is not supported for claude-sonnet-4-6". In some SDK versions with streaming, the error surfaces differently, but the HTTP status is always 400.
Is output_config.format the same as JSON mode?
No. JSON mode ({"type": "json_object"}) tells Claude to output valid JSON of any structure. output_config.format with json_schema validates the output against your specific schema using constrained decoding — Claude physically cannot produce output that violates the schema. The latter is strictly more reliable for structured extraction.
Can I use Instructor instead of migrating to output_config.format?
Yes. Instructor wraps the Anthropic client and uses tool use internally, which never relied on prefill. Migrating from direct Anthropic client calls to Instructor is a valid migration path and avoids managing the output_config schema manually.
Does this affect Claude via AWS Bedrock or Vertex AI?
If you're accessing Claude via Bedrock or Vertex, the same model versions apply. Claude Sonnet 4.6 on Bedrock has the same prefill restriction as the direct Anthropic API. Check your Bedrock/Vertex SDK for equivalent output_config support — it may be exposed differently in the managed platform wrappers.
Still dealing with broken JSON?
Paste it in and get it fixed in under 1 second — free, no signup, no install. Works with ChatGPT, Claude, n8n, and any AI output.
Fix My JSON Free →Related Articles