Skip to content

fix(core): handle NotRequired/Required in TypedDict OpenAI function schema conversion#36188

Closed
Alvin Tang (alvinttang) wants to merge 1 commit intolangchain-ai:masterfrom
alvinttang:fix/typeddict-notrequired-schema
Closed

fix(core): handle NotRequired/Required in TypedDict OpenAI function schema conversion#36188
Alvin Tang (alvinttang) wants to merge 1 commit intolangchain-ai:masterfrom
alvinttang:fix/typeddict-notrequired-schema

Conversation

@alvinttang
Copy link
Copy Markdown

Description

Fixes #34085.

When a TypedDict with NotRequired[X] or Required[X] fields is passed to
convert_to_openai_function() (or .with_structured_output()), a TypeError
is raised inside _convert_any_typed_dicts_to_pydantic:

TypeError: NotRequired accepts only a single type. Got (<class 'str'>,).

Root cause

_convert_any_typed_dicts_to_pydantic has two problems:

  1. Generic type handler (line 278): when it encounters NotRequired[str] as
    a generic type it rebuilds it as NotRequired[(str,)] (tuple), but
    NotRequired / Required are single-argument special forms and reject tuples.

  2. TypedDict field loop: get_type_hints(include_extras=True) preserves
    NotRequired[X] wrappers on fields. The loop never stripped this wrapper, so
    the raw NotRequired[str] fell through to the generic handler and triggered the
    bug above. Additionally, NotRequired fields were incorrectly marked as
    required (default=...) in the Pydantic v1 model.

Fix

In langchain_core/utils/function_calling.py:

  1. Field loop — before processing each annotation, unwrap NotRequired[X]/
    Required[X] and use default=None for NotRequired fields (and total=False
    fields without an explicit Required wrapper) so they are absent from the
    generated JSON schema required array.

  2. Generic handler — when origin is NotRequired or Required, index with
    the single unwrapped arg (type_args[0]) rather than the full tuple.

Testing

Three new parameterised test cases (each run against both typing.TypedDict
and typing_extensions.TypedDict):

  • test_convert_typed_dict_notrequired_fields — exact repro from the issue
  • test_convert_typed_dict_required_fields_total_falseRequired[] on a
    total=False TypedDict
  • test_convert_typed_dict_notrequired_nested_typeNotRequired[list[str]]
    (nested generic, the direct trigger of the original TypeError)

All 36 existing tests in test_function_calling.py continue to pass.

Checklist

  • Tests added
  • No regressions (full test_function_calling.py suite passes)
  • Follows existing code style

When a TypedDict with NotRequired[X] or Required[X] fields was passed to
convert_to_openai_function(), a TypeError was raised because
_convert_any_typed_dicts_to_pydantic() tried to re-subscript NotRequired
with a tuple of args instead of a single type.

Two fixes in langchain_core/utils/function_calling.py:

1. TypedDict field loop: unwrap NotRequired/Required wrappers before
   processing the inner type, and set default=None for NotRequired fields
   (and total=False fields without a Required wrapper) so they are omitted
   from the generated JSON schema "required" list.

2. Generic type handler: when origin is NotRequired or Required (single-arg
   special forms), pass type_args[0] instead of the whole tuple to avoid
   "NotRequired accepts only a single type. Got (<class ...>,)".

Fixes langchain-ai#34085

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added core `langchain-core` package issues & PRs fix For PRs that implement a fix size: S 50-199 LOC labels Mar 24, 2026
@github-actions
Copy link
Copy Markdown

This PR has been automatically closed because you are not assigned to the linked issue.

External contributors must be assigned to an issue before opening a PR for it. Please:

  1. Comment on the linked issue to request assignment from a maintainer
  2. Once assigned, edit your PR description and the PR will be reopened automatically

Maintainers: reopen this PR or remove the missing-issue-link label to bypass this check.

@github-actions github-actions Bot closed this Mar 24, 2026
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 external fix For PRs that implement a fix missing-issue-link new-contributor size: S 50-199 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NotRequired in TypedDict causes TypeError when converting to OpenAI function schema

1 participant