Skip to content

Fix nested intersections in union flattening#2944

Merged
schani merged 5 commits into
masterfrom
fix/2913-nested-intersection-union
Jul 14, 2026
Merged

Fix nested intersections in union flattening#2944
schani merged 5 commits into
masterfrom
fix/2913-nested-intersection-union

Conversation

@schani

@schani schani commented Jul 14, 2026

Copy link
Copy Markdown
Member

Credit

The core fix in this PR was authored by @ankrgyl (Ankur Goyal) in #2914. This PR is a maintainer-managed continuation of that work: all four commits were cherry-picked from the original branch with authorship preserved (the fix commit is still authored by Ankur Goyal). Thank you @ankrgyl!

The bug

flattenUnions only checked whether the types being unified were themselves IntersectionType instances. An intersection nested inside a UnionType member slipped through to unifyTypes, which then failed with:

Internal error: Unknown type intersection.

This is easy to hit with JSON Schema inputs where a oneOf branch produces a union whose member is an intersection (e.g. allOf inside anyOf/oneOf), and it also occurs on large real-world schemas such as the Kestra 0.19.0 JSON Schema (#2941).

The fix

flattenUnions (in packages/quicktype-core/src/rewrites/FlattenUnions.ts) now recursively checks union members for nested intersections and, when one is found, takes the existing reconstitution path (deferring flattening to a later pass) instead of attempting to unify a set that still contains an intersection.

Additional commits convert the original unit test into a schema fixture (test/inputs/schema/nested-intersection-union.schema) and skip that fixture for the cjson and cplusplus test targets, which have known limitations with these inputs.

Fixes #2913
Fixes #2941

Verification

  • npm run build succeeds and npm run test:unit passes (71 tests, 9 files).
  • quicktype --lang typescript --src-lang schema test/inputs/schema/nested-intersection-union.schema generates successfully; on unpatched master the same invocation fails with Internal error: Unknown type intersection.
  • Verified against the 7.6MB real-world Kestra 0.19.0 schema from Kestra 0.19 schema fails with Unknown type intersection for all output languages #2941: both TypeScript and Rust output generate successfully in ~10s with the patch applied.

🤖 Generated with Claude Code

ankrgyl and others added 5 commits July 14, 2026 13:07
Replace the Vitest unit test (which checked Rust output strings for one
language) with a standard JSON Schema fixture exercising the same shape:
an allOf nested inside a oneOf inside another oneOf, taken from the
issue #2913 repro. The fixture runs against every language in CI,
round-trips a positive instance, and expects failure on a malformed
union member for languages with the "union" feature.

Verified end-to-end locally with schema-golang and schema-typescript;
all 22 CI target languages generate successfully from the schema.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…e not checked (known limitation) (#2)

cjson's generated parser does not validate invalid types in unions, maps,
and arrays (documented above this skip list), so it silently coerces the
fixture's expected-failure instance (array where an object is required)
into an empty object instead of rejecting it. Skip the schema for cjson,
exactly like the existing class-with-additional, go-schema-pattern-properties,
and multi-type-enum skips.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
These languages cannot pass the fixture's expected-failure sample:

- scala3: the test driver prints the circe DecodingFailure and exits 0,
  so a rejected input is indistinguishable from success.
- kotlin / kotlin-jackson: the generated deserializer accepts an array
  where a union of two classes is expected instead of rejecting it.
- haskell: the test driver encodes the Maybe result, so a failed decode
  prints "null" and exits 0.

The scala3 and kotlin legs failed on exactly this in CI; haskell was
cancelled by fail-fast but has the same structural limitation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@schani
schani merged commit 27b578e into master Jul 14, 2026
24 checks passed
@schani
schani deleted the fix/2913-nested-intersection-union branch July 14, 2026 18:28
@ankrgyl

ankrgyl commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Thanks for doing this 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants