Skip to content

Fix "Type creator didn't return its forwarding ref" crash on sibling maps#2877

Merged
schani merged 2 commits into
masterfrom
fix/unify-map-forwarding-ref
Jul 6, 2026
Merged

Fix "Type creator didn't return its forwarding ref" crash on sibling maps#2877
schani merged 2 commits into
masterfrom
fix/unify-map-forwarding-ref

Conversation

@schani

@schani schani commented Jul 6, 2026

Copy link
Copy Markdown
Member

Problem

Valid JSON input with sibling map-typed fields — where one map has entries and a sibling map is empty — crashes quicktype for every target language with:

Error: Internal error: Type creator didn't return its forwarding ref.

(The web app swallows the error, so it just silently produces nothing.) Minimal 126-byte repro:

{"mission_specs": {"1": {"objectives": {"2": {"rewards": {"3": {}, "5": {}}}}}, "4": {"objectives": {"6": {"rewards": {}}}}}}

The only workaround was --no-maps.

Root cause

In UnifyClasses.ts, UnifyUnionBuilder.makeObject has three branches for combining object types. The class branch (getUniqueClassType) and the object branch (getUniqueObjectType) both pass the pending forwardingRef through, but the map branch called this.typeBuilder.getMapType(typeAttributes, ...) without it. When the inferMaps rewrite unifies sibling classes into a map, the newly created map type gets a fresh ref instead of consuming the forwarding ref, tripping the assert in GraphRewriteBuilder.withForwardingRef (GraphRewriting.ts).

Fix

One line: pass forwardingRef to getMapType in the map branch, matching the other two branches (getMapType already accepts an optional forwardingRef parameter).

Tests

Test first: the minimized JSON was added as test/inputs/json/priority/bug2037.json before the fix, and FIXTURE=golang script/test test/inputs/json/priority/bug2037.json was run against the unpatched build — it failed with the exact crash (Internal error: Type creator didn't return its forwarding ref, thrown from GraphRewriteBuilder.withForwardingRef via UnifyUnionBuilder.makeObject). After the fix, the same run passes (Go generation, compile, and JSON round-trip).

Since priority/ inputs run for all languages, this fixture guards the fix everywhere.

Verification

  • Post-fix, QUICKTEST=1 FIXTURE=golang script/test (all 41 priority/schema samples incl. the new one) passes locally — no regressions from the new priority input.
  • The original 1.1 MB sync.txt attachment from the issue, plus all minimized variants from triage, now convert successfully (verified with --lang csharp).
  • Generation-level spot check across typescript, rust, python, swift, java, kotlin, csharp, dart, php, and elm: all succeed on the new input.
  • Not verified locally: compile/round-trip fixtures for toolchains not installed here (C#, Java, Kotlin, Dart, PHP, …) — CI covers those since the input is in priority/.

This also fixes the duplicates #1320 and #1509 (same forwarding-ref crash).

Fixes #2037

🤖 Generated with Claude Code

UnifyUnionBuilder.makeObject's map branch called getMapType without the
pending forwardingRef, while the class/object branches pass it through.
When inferMaps rewrites sibling classes that unify into a map (e.g. a
map-typed field whose values contain both non-empty and empty maps),
the type creator then returned a ref different from its forwarding ref,
tripping the "Type creator didn't return its forwarding ref" assert in
GraphRewriteBuilder.withForwardingRef and crashing for every target
language.

Add the minimized JSON from issue #2037 as a priority regression
fixture; it crashed before this fix and now converts for all languages.

Fixes #2037

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@schani
schani merged commit d298fba into master Jul 6, 2026
22 checks passed
@schani
schani deleted the fix/unify-map-forwarding-ref branch July 6, 2026 21:27
pull Bot pushed a commit to meonBot/quicktype that referenced this pull request Jul 14, 2026
…ps#2881)

Maps of empty objects render as Map<String, X> with typealias X =
JsonObject, but Klaxon's reflective deserializer never consults custom
converters for map values, so parsing fails with "Couldn't find a
suitable constructor for class JsonObject to initialize with {}".

Adds unit tests expecting such fields to be handled by a field-level
converter, and re-enables the bug2037.json fixture for Kotlin/Klaxon
that glideapps#2877 had to skip. The unit tests currently fail; the fix follows
in the next commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

valid json, unable to convert to C# or nothing seems to happen, default settings

1 participant