Fix: Anthropic API 500 Error with strict: true Tools
The Error
When using strict: true with the structured outputs beta, the API returns a 500 Internal Server Error for tool schemas with complex nested structures:
{
"type": "error",
"error": {
"type": "api_error",
"message": "Internal server error"
},
"request_id": "req_011CXYasMqMAq6EP1aGLry8N"
}
The same request works perfectly when strict: true is removed. The error occurs after 17-55 seconds of processing, suggesting a server-side crash during grammar compilation.
Quick Fix
Remove strict: true from tools with complex nested schemas:
const tools = [{
name: "create_booking",
// strict: true, // Remove this
input_schema: {
type: "object",
properties: { /* ... complex schema ... */ },
required: ["field1", "field2"],
additionalProperties: false
}
}];
The model will still follow the schema in most cases. Validate the output yourself.
What’s Happening
When you set strict: true on a tool, the API compiles a constrained decoding grammar from your JSON Schema. This grammar ensures the model’s output is guaranteed to be valid according to your schema. The compilation process converts the JSON Schema into a finite-state automaton.
For complex schemas, this compilation can fail or exceed internal limits. The factors that cause grammar explosion:
1. Nested Arrays of Objects
// This pattern causes exponential grammar state growth:
{
accommodationRate: {
type: "array",
items: {
type: "object",
properties: {
roomId: { type: "string" },
price: { type: "integer" }
},
required: ["roomId", "price"],
additionalProperties: false
}
}
}
Each nested object with required fields and additionalProperties: false creates branching points in the grammar.
2. Multiple Complex Arrays in One Schema
Test results confirm the pattern:
| Configuration | Result |
|---|---|
1 simple tool + strict: true |
Works (7.8s) |
2 simple tools + strict: true |
Works (8.2s) |
3 tools (including complex) + strict: true |
500 Error (17.3s) |
Same 3 tools WITHOUT strict: true |
Works (5.8s) |
Complex tool + strict: true (no thinking) |
500 Error |
3. The Grammar Size Limit
The related error “compiled grammar is too large” (which returns a 400 instead of 500) triggers when the grammar exceeds a documented size threshold. The 500 error appears to be an unhandled edge case where the grammar compilation itself crashes before the size check.
Step-by-Step Solution
Option 1: Simplify the Schema
Flatten nested structures by using string-encoded JSON for complex inner types:
// BEFORE: Complex nested schema (causes 500)
const schema = {
type: "object",
properties: {
rooms: {
type: "array",
items: {
type: "object",
properties: {
id: { type: "string" },
rate: { type: "integer" },
extras: {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string" },
cost: { type: "integer" }
},
required: ["name", "cost"]
}
}
},
required: ["id", "rate"]
}
}
}
};
// AFTER: Flattened schema (works with strict: true)
const schema = {
type: "object",
properties: {
rooms: {
type: "array",
items: {
type: "object",
properties: {
id: { type: "string" },
rate: { type: "integer" },
extras_json: { type: "string" } // JSON-encoded array
},
required: ["id", "rate", "extras_json"],
additionalProperties: false
}
}
},
required: ["rooms"],
additionalProperties: false
};
// Then parse extras_json yourself:
const extras = JSON.parse(result.rooms[0].extras_json);
Option 2: Split Into Multiple Tools
Instead of one tool with a complex schema, split it into sequential calls:
const tools = [
{
name: "create_booking_base",
strict: true,
input_schema: {
type: "object",
properties: {
arrival: { type: "string" },
departure: { type: "string" },
firstName: { type: "string" },
lastName: { type: "string" },
numAdults: { type: "integer" }
},
required: ["arrival", "departure", "firstName", "lastName", "numAdults"],
additionalProperties: false
}
},
{
name: "add_room_rates",
strict: true,
input_schema: {
type: "object",
properties: {
bookingId: { type: "string" },
roomId: { type: "string" },
price: { type: "integer" }
},
required: ["bookingId", "roomId", "price"],
additionalProperties: false
}
}
];
Option 3: Selective strict: true
Apply strict: true only to simple tools, and validate complex tools manually:
import { z } from "zod";
// Simple tool — use strict
const checkAvailability = {
name: "check_availability",
strict: true,
input_schema: {
type: "object",
properties: {
startDate: { type: "string" },
endDate: { type: "string" }
},
required: ["startDate", "endDate"],
additionalProperties: false
}
};
// Complex tool — skip strict, validate with Zod
const createBooking = {
name: "create_booking",
// No strict: true
input_schema: { /* complex schema */ }
};
// Manual validation for non-strict tools:
const BookingSchema = z.object({
rooms: z.array(z.object({
id: z.string(),
rate: z.number(),
extras: z.array(z.object({
name: z.string(),
cost: z.number()
}))
}))
});
function handleToolCall(name: string, input: unknown) {
if (name === "create_booking") {
const parsed = BookingSchema.safeParse(input);
if (!parsed.success) {
return { error: `Invalid input: ${parsed.error.message}` };
}
return processBooking(parsed.data);
}
}
Option 4: Use output_config Instead
For single-response structured output (not tool use), output_config may handle complex schemas better:
const response = await client.messages.create({
model: "claude-sonnet-4-5-20250929",
max_tokens: 4096,
messages: [{ role: "user", content: "Create a booking for..." }],
output_config: {
format: {
type: "json_schema",
schema: complexSchema
}
}
});
Prevention
- Keep strict tool schemas flat — avoid more than 2 levels of nesting
- Limit the number of strict tools to 3-5 per request
- Test schema complexity incrementally — add properties one at a time and test
- Use string-encoded JSON for deeply nested sub-structures
- Always have a non-strict fallback with manual validation via Zod or similar
Related Issues
- Fix: “Compiled grammar is too large” Error
- Fix: Claude API Error 400 Invalid Request
- Advanced Claude Skills with Tool Use and Function Calling
Tools That Help
When debugging complex API integrations with structured outputs, a dev tool extension can help you inspect request/response payloads and identify which schema patterns trigger server-side failures.