Skip to content

fix(json_schema): make prefixItems entries positional per Draft 2020-12 - #834

Merged
Seven-Streams merged 2 commits into
mlc-ai:mainfrom
feiiiiii5:fix/prefix-items-positional-824
Aug 31, 2026
Merged

Seven-Streams merged 2 commits into
mlc-ai:mainfrom
feiiiiii5:fix/prefix-items-positional-824

Conversation

@feiiiiii5

Copy link
Copy Markdown
Contributor

Root Cause

GenerateArray built the prefix as Sequence(prefix_elements), forcing every prefixItems entry to appear. Per JSON Schema Draft 2020-12, prefixItems entries are positional — an instance may validly end after any prefix position (subject to minItems) — so arrays shorter than the prefix were wrongly rejected ([] and ["a"] for a two-item prefix), and with a naive optional-chain fix the additional-items tail could absorb prefix positions (issue #824).

Fix

Build the array content as an alternation of the full prefix (plus the additional tail) and every shorter truncation, keeping the first min(minItems, n) entries mandatory:

content = full_prefix (+ additional tail)*
        | truncation ending at each position in [min(minItems, n), n)

Additional items are only reachable after the complete prefix, so a mismatch inside the prefix range can never be rescued by the additional-items path. Truncations are plain sequences (no optional-chain backtracking), which keeps the grammar unambiguous.

Test

  • 3 new regression tests in tests/python/test_json_schema_converter.py:
    • test_prefix_items_truncated_prefix_accepted: the issue's accept-set matrix (1111 for [], ["a"], ["a",1], ["a",1,true] with items: {}), using the discriminating empty/one-element fixtures.
    • test_prefix_items_min_items_forces_prefix_head: minItems still forces the head of the prefix.
    • test_prefix_items_no_additional_items_allows_shorter: items: false forbids elements beyond the prefix but must not force the full length.
  • Updated 3 existing tests that pinned the old behavior (test_non_strict, test_array_schema additionalItems/unevaluatedItems cases, test_array_schema_min_max): shorter prefixes now accepted, wrong-position values still rejected.
  • Local: full tests/python suite 3253 passed (694 skipped, HF_TOKEN-gated), ruff check clean, black 24.1.0 / clang-format 19.1.7 clean.

Diff scope

2 files, +115/-20 (cpp/json_schema_converter.cc GenerateArray block; tests/python/test_json_schema_converter.py).

AI Disclosure

Root-cause analysis and the first optional-chain draft were AI-assisted; that draft over-accepted (additional items absorbing prefix positions), which was caught by the regression matrix and replaced with the truncation-alternation construction. The final patch was built and verified locally (cmake+ninja, full python suite) and human-reviewed before submission.

Fixes #824

Comment thread cpp/json_schema_converter.cc Resolved
Comment thread cpp/json_schema_converter.cc Resolved
GenerateArray forced the full prefixItems sequence, so arrays shorter
than the prefix were rejected (e.g. [] and [a] for a two-item prefix),
and elements past a truncated prefix could leak into the additional-items
tail. Build the array content as an alternation of the full prefix (plus
the additional tail) and every shorter truncation, with the first
min(minItems, n) entries still mandatory.

Fixes mlc-ai#824.

Signed-off-by: fei <[email protected]>
The truncation alternatives each repeated the mandatory head and the
shared prefix of the remaining items, so an n-item prefix expanded to
O(n^2) rule references. Replace the alternation with a right-recursive
chain   suffix_k ::= "" | sep item_k suffix_{k+1}   ending in the
additional-items tail, so each position is encoded once. Position 0,
when it is not part of the mandatory head, gets its own rule without
the separator. Also condenses the block comment.

The accepted language is unchanged; the full Python test suite passes.

Signed-off-by: fei <[email protected]>
@Seven-Streams
Seven-Streams force-pushed the fix/prefix-items-positional-824 branch from 3f99770 to 83f1125 Compare August 29, 2026 14:57
@Seven-Streams
Seven-Streams merged commit a1a3904 into mlc-ai:main Aug 31, 2026
47 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] prefixItems without minItems requires all prefix elements

2 participants