Skip to content

fix(anthropic): coerce explicit additionalProperties to false in output_format schema - #35811

Merged
mateo-berri merged 1 commit into
BerriAI:litellm_internal_stagingfrom
dkindlund:fix/anthropic-output-format-additional-properties
Aug 6, 2026
Merged

fix(anthropic): coerce explicit additionalProperties to false in output_format schema#35811
mateo-berri merged 1 commit into
BerriAI:litellm_internal_stagingfrom
dkindlund:fix/anthropic-output-format-additional-properties

Conversation

@dkindlund

@dkindlund dkindlund commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Fixes #35808

Pre-Submission checklist

  • I have added meaningful tests
  • My PR passes all CI/CD checks (lint, format, unit tests) — ruff format clean, target suite green
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5

Screenshots / Proof of Fix

Root cause

Anthropic's structured outputs reject any additionalProperties value other than false. Their docs list under Supported features: "required and additionalProperties (must be set to false for objects)", and under Not supported: "additionalProperties set to anything other than false"

filter_anthropic_output_schema only added the key when it was absent:

if result.get("type") == "object" and "additionalProperties" not in result:
    result["additionalProperties"] = False

additionalProperties isn't in the unsupported/strip set, so an explicit value is copied verbatim by the catch-all else: result[key] = value, and the not in result guard never fires. The explicit value reaches output_format.schema and 400s

This PR coerces the value for object schemas instead. That is what the official SDKs do — anthropic-sdk-python _parse/_transform.py (pop(...) then strict_schema["additionalProperties"] = False) and anthropic-sdk-typescript transform-json-schema.ts

The permissive tool-use path (map_response_format_to_anthropic_tool, which sets _input_schema["additionalProperties"] = True and is deliberately forced for vertex_ai per #18625 / #19201) is not touched

Before (on litellm_internal_staging, v1.96.0)

from litellm.llms.anthropic.chat.transformation import AnthropicConfig
AnthropicConfig.filter_anthropic_output_schema(
    {"type": "object", "additionalProperties": True, "properties": {"a": {"type": "string"}}}
)
# -> {'type': 'object', 'additionalProperties': True, 'properties': {...}}   <-- 400s at Anthropic

Every explicit form leaked through — top level, nested under properties, inside array items, and the dict sub-schema form:

top-level explicit true    -> leaked non-false additionalProperties: [True]
nested under properties    -> leaked non-false additionalProperties: [True]
inside array items         -> leaked non-false additionalProperties: [True]
dict sub-schema form       -> leaked non-false additionalProperties: [{'type': 'string'}]
control: key absent        -> leaked non-false additionalProperties: NONE (ok)

End-to-end against a real azure_ai/claude-sonnet-4-6 deployment (un-mocked, real API call), sending a schema with "additionalProperties": true:

HTTP 400
litellm.BadRequestError: Azure_aiException - {"type":"error","error":{"type":"invalid_request_error",
"message":"output_format.schema: For 'object' type, 'additionalProperties: true' is not supported.
Please set 'additionalProperties' to false"},"request_id":"req_011CdiGH686teY8qpkxwe2mP"}

After (commit 46751ad)

All five cases clean:

top-level true   -> NONE (ok)
nested           -> NONE (ok)
array items      -> NONE (ok)
sub-schema       -> NONE (ok)
control absent   -> NONE (ok)
$ uv run pytest tests/litellm/llms/anthropic/test_anthropic_schema_filter.py -q
21 passed

Regression check on the surrounding suites (tests/litellm/llms/anthropic/ + tests/test_litellm/llms/anthropic/): 41 failed / 1218 passed with this change vs 41 failed / 1214 passed on the unmodified branch — identical pre-existing failure set, +4 new passing tests. (The pre-existing failures are unrelated experimental_pass_through/context_management tests; a tests/test_litellm/llms/vertex_ai/realtime collection error is a missing local websockets dep and also reproduces unmodified)

Note on the live "after": reproducing the 400 requires addressing the Azure deployment directly — if the model group has a fallback to a vertex_ai deployment, LiteLLM transparently retries there and returns 200, masking the rejection

Type

🐛 Bug Fix

Changes

  • litellm/llms/anthropic/chat/transformation.py — in filter_anthropic_output_schema, drop the and "additionalProperties" not in result guard so object schemas always get additionalProperties: False (applies at every recursion depth and to the dict sub-schema form)
  • tests/litellm/llms/anthropic/test_anthropic_schema_filter.py — 4 tests: explicit true at top level, nested (under properties and inside array items), the dict sub-schema form, and an explicit false preserved

Semantic caveat (disclosed)

Coercing narrows what the schema permits. For a bare free-form map ({"type": "object", "additionalProperties": true} with no properties) this means "object with no allowed keys", so the model can only emit {} — genuine semantic loss. Anthropic has no native encoding for that shape and the official SDKs degrade it identically; the alternative on this path is a guaranteed 400. A more conservative future option would be detecting that shape and routing it to the permissive tool-use path (the one already forced for vertex_ai), but that is a larger change and out of scope here

Prior art

@greptile-apps

greptile-apps Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR updates Anthropic structured-output schema transformation to force additionalProperties to false for every object schema.

  • Handles explicit true and dictionary-valued forms at all recursion depths.
  • Adds regression coverage for top-level, nested, array-item, sub-schema, and already-false cases.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains, and the previously reported comment-convention issue is fixed on the current HEAD.

Important Files Changed

Filename Overview
litellm/llms/anthropic/chat/transformation.py Always normalizes object schemas to Anthropic's required additionalProperties: false; the previously flagged production comment expansion has been fully reverted.
tests/litellm/llms/anthropic/test_anthropic_schema_filter.py Adds focused regression tests covering explicit and recursively nested additionalProperties values.

Reviews (2): Last reviewed commit: "fix(anthropic): coerce explicit addition..." | Re-trigger Greptile

Comment thread litellm/llms/anthropic/chat/transformation.py Outdated
…ut_format schema

Anthropic's structured outputs reject any `additionalProperties` value other
than `false` ("output_format.schema: For 'object' type, 'additionalProperties:
true' is not supported. Please set 'additionalProperties' to false")

`filter_anthropic_output_schema` only added the key when it was absent, so an
explicit `true` (or a sub-schema) was copied verbatim into output_format.schema
and 400'd. Coerce it for object schemas instead, at every recursion depth,
matching what the Anthropic Python/TypeScript SDKs do

The permissive tool-use path (map_response_format_to_anthropic_tool, used for
vertex_ai) is deliberately left alone

Fixes BerriAI#35808
@dkindlund
dkindlund force-pushed the fix/anthropic-output-format-additional-properties branch from 37607ad to 46751ad Compare August 4, 2026 20:21
@dkindlund

Copy link
Copy Markdown
Contributor Author

@greptileai addressed in 46751ad — comment reverted to the original two lines, plus the other CLAUDE.md violations I'd missed (commit trailer, PR-body attribution, trailing periods). Please re-review

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed-hq

codspeed-hq Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing dkindlund:fix/anthropic-output-format-additional-properties (46751ad) with litellm_internal_staging (355ae99)1

Open in CodSpeed

Footnotes

  1. No successful run was found on litellm_internal_staging (dcb4e50) during the generation of this report, so 355ae99 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@mateo-berri mateo-berri left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the contribution!

@mateo-berri
mateo-berri merged commit c1fa151 into BerriAI:litellm_internal_staging Aug 6, 2026
77 of 78 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: explicit additionalProperties: true is forwarded to Anthropic output_format.schema and 400s (filter only fills in when absent)

2 participants