Skip to content

fix(core): strictify schemas inside tuple prefixItems - #4694

Closed
XuQuanxin04 wants to merge 1 commit into
openai:mainfrom
XuQuanxin04:fix/strict-schema-prefixitems
Closed

fix(core): strictify schemas inside tuple prefixItems#4694
XuQuanxin04 wants to merge 1 commit into
openai:mainfrom
XuQuanxin04:fix/strict-schema-prefixitems

Conversation

@XuQuanxin04

Copy link
Copy Markdown

Summary

ensure_strict_json_schema recursed into items, anyOf, oneOf, allOf, properties, and $defs, but it never visited prefixItems. pydantic emits prefixItems for fixed-length tuple type hints, so a non-strict subschema hidden inside a tuple element was passed through unchanged.

Concretely, a dict[str, int] parameter is rejected at the top level because its additionalProperties is a schema rather than false:

additionalProperties should not be set for object types. This could be because you're using an older version of Pydantic

But the same type wrapped in a tuple — def f(data: tuple[dict[str, int], str]) — silently produced:

"prefixItems": [
  { "type": "object", "additionalProperties": { "type": "integer" } },
  { "type": "string" }
]

That schema is not valid for OpenAI Structured Outputs, so the tool/structured-output call is rejected by the API at request time.

Fix

Recurse into each prefixItems entry exactly like array items, so tuple elements are strictified consistently. $defs are still strictified at the root, variable-length tuples (tuple[int, ...]) already use items and were already handled, and primitive/model tuples remain valid.

Test plan

Added two tests to tests/test_strict_schema.py:

  • test_prefix_items_entries_are_strictified — an object entry inside prefixItems gets additionalProperties: false and a required list.
  • test_open_object_inside_prefix_items_is_rejected_like_top_level — an additionalProperties schema inside a tuple element raises the same UserError as the top-level case.

Verified directly with function_schema:

  • tuple[dict[str, int], str] now raises the same UserError (before: silently non-strict).

  • tuple[Coord, str], tuple[str, int, bool], tuple[Pt, Pt], and tuple[int, ...] all produce valid strict schemas.

  • pytest tests/test_strict_schema.py tests/test_function_schema.py tests/test_function_tool.py — 172 passed

  • pytest tests/models/test_openai_responses.py tests/models/test_openai_chatcompletions.py tests/models/test_openai_chatcompletions_stream.py — 317 passed

  • ruff format --check and ruff check clean on changed files.

No issue number; self-contained strict-schema correctness fix.

Checks

  • I've added new tests, if relevant
  • I've run .agents/skills/code-change-verification/scripts/run.sh — could not run the bash script end-to-end on this Windows/Git-Bash host (POSIX process-group management via setsid/kill -PID); I ran its constituent steps directly (ruff format, ruff check, and the relevant pytest suites above).
  • I've confirmed all verification steps pass

ensure_strict_json_schema recursed into items, anyOf, oneOf, allOf,
properties, and $defs, but never into prefixItems. pydantic emits
prefixItems for fixed-length tuple type hints, so a non-strict schema
hidden inside a tuple element was passed through unchanged: for example
a dict[str, int] parameter was rejected at the top level (its
additionalProperties is a schema), but the same type wrapped in a tuple
silently produced a non-strict schema that the OpenAI Structured Outputs
API rejects.

Recurse into each prefixItems entry exactly like array items, so tuple
elements are strictified consistently. $defs are still strictified at
the root, and variable-length tuples continue to use items.

Co-Authored-By: Claude <noreply@anthropic.com>
@seratch seratch changed the title Strictify schemas inside tuple prefixItems fix(core): strictify schemas inside tuple prefixItems Aug 27, 2026

@seratch seratch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The prefixItems recursion is the correct implementation direction. Please add one caller-visible regression that starts from an actual annotated function or Pydantic model, runs through function_schema(), and verifies that an object nested in a fixed tuple is strictified. The current hand-written schema tests prove the helper recursion but not the public schema-generation path that exposed the bug.

@seratch

seratch commented Aug 27, 2026

Copy link
Copy Markdown
Member

Thanks for the focused strict-schema fix. #4696 now contains the same prefixItems traversal together with the originating fixed-tuple function-schema behavior and public-boundary regression coverage.

We should merge only one source of truth for this change, so I am closing this PR as superseded by #4696.

@seratch seratch closed this Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants