Skip to content

fix(core): require all nested properties for strict tool schemas - #39306

Merged
ccurme (ccurme) merged 11 commits into
masterfrom
imnishitha/core/strict-nested-required
Aug 18, 2026
Merged

fix(core): require all nested properties for strict tool schemas#39306
ccurme (ccurme) merged 11 commits into
masterfrom
imnishitha/core/strict-nested-required

Conversation

@imnishitha

@imnishitha Nishitha M (imnishitha) commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Closes #33869, #38223

Issue:

OpenAI strict tool schemas fail when nested objects contain properties that are optional

class Filters(BaseModel):
    category: str | None = None
    active: bool | None = None


class Ingredient(BaseModel):
    name: str
    amount: int
    filters: Filters | None = None


class Recipe(BaseModel):
    ingredients: list[Ingredient]


my_tool = convert_to_openai_tool(Recipe, strict=True)

model = init_chat_model("openai:gpt-5.4", use_responses_api=True)
response = model.bind_tools([my_tool], strict=True).invoke("Give me a recipe for Banana pudding. It should have 3 ingredients. Call the tool.")

Throws:

Output:

openai.BadRequestError: Error code: 400 - {'error': {'message': "Invalid schema for function 'browser_console_messages': In context=(), 'required' is required to be supplied and to be an array including every key in properties. Missing 'onlyErrors'.", 'type': 'invalid_request_error', 'param': 'tools[2].function.parameters', 'code': 'invalid_function_parameters'}}
During task with name 'model' and id '708d0a1a-0bb5-1d86-e7d7-b37d6e41009a'

Fix:

convert_to_openai_function(strict=True) now recursively adds all object properties to required, matching OpenAI's strict schema requirements.

Nested object schemas (e.g. from MCP tools) only had their top-level
properties added to `required` when `strict=True`. OpenAI's strict mode
requires every property at every nesting level to be listed in
`required`, so nested optional fields caused the API to reject the
request outright.

Closes #33869

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
@github-actions github-actions Bot added core `langchain-core` package issues & PRs fix For PRs that implement a fix internal size: XS < 50 LOC labels Aug 6, 2026
@codspeed-hq

codspeed-hq Bot commented Aug 6, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 15 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing imnishitha/core/strict-nested-required (3d6ae7c) with master (94509fa)2

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on master (32c15bb) during the generation of this report, so 94509fa was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@imnishitha
Nishitha M (imnishitha) marked this pull request as ready for review August 6, 2026 23:00
@imnishitha Nishitha M (imnishitha) changed the title fix(core): require all nested properties for strict tool schemas [closes #33869] fix(core): require all nested properties for strict tool schemas Aug 6, 2026

@open-swe open-swe Bot 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.

Open SWE Review found 1 potential issue.

Open in WebView Open SWE trace

Comment thread libs/core/langchain_core/utils/function_calling.py
Address review feedback on #39306: the recursive strict-mode walk only
followed anyOf/properties/items, so nested object schemas referenced
via $ref into a top-level $defs/definitions map (as raw JSON-schema
tool input can represent them) were skipped, leaving them without
required/additionalProperties and still rejected under strict mode.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
@github-actions github-actions Bot added size: S 50-199 LOC and removed size: XS < 50 LOC labels Aug 7, 2026
@langchain-ai LangChain (langchain-ai) deleted a comment from open-swe Bot Aug 7, 2026
Nishitha M (imnishitha) and others added 3 commits August 6, 2026 21:48
Fixes CI failure on the pydantic ~=2.7.0 matrix job: Pydantic <2.9
wraps a referenced model field in "allOf" when it has sibling keys
(e.g. a Field description), instead of merging them directly like
2.9+ does. The strict-mode recursive walk didn't follow "allOf", so
the referenced object schema never got its required/additionalProperties
completed under older pydantic versions, leaving the regression test
for #33869 failing with a KeyError.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Comment thread libs/core/langchain_core/utils/function_calling.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

left a few questions, thanks!

_recursive_set_additional_properties_false(schema["items"])
# Raw JSON schemas may keep nested objects in `$defs` and reference them via
# `$ref`; walk those definitions too so they're made strict.
for defs_key in ("$defs", "definitions"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why $defs and definitions?

Comment on lines +824 to +827
# OpenAI strict mode requires every property to appear in `required` at every
# level of nesting. Without this, nested object schemas are rejected.
properties = schema.get("properties")
if isinstance(properties, dict) and properties:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

should we instead be rejecting these tools then?
ccurme (@ccurme) are we in agreement that we should be mutating schemas?

@ccurme
ccurme (ccurme) enabled auto-merge (squash) August 18, 2026 17:08
@ccurme
ccurme (ccurme) merged commit 65e5e3c into master Aug 18, 2026
99 checks passed
@ccurme
ccurme (ccurme) deleted the imnishitha/core/strict-nested-required branch August 18, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core `langchain-core` package issues & PRs fix For PRs that implement a fix internal size: S 50-199 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[langchain Agent]: Errors in tool calling from Playwright MCP

3 participants