Skip to content

fix(java): emit guarded undiscriminated-union members before all-optional ones in deserializer#15859

Merged
iamnamananand996 merged 1 commit into
mainfrom
fix/java-undiscriminated-union-all-optional-ordering
May 12, 2026
Merged

fix(java): emit guarded undiscriminated-union members before all-optional ones in deserializer#15859
iamnamananand996 merged 1 commit into
mainfrom
fix/java-undiscriminated-union-all-optional-ordering

Conversation

@iamnamananand996
Copy link
Copy Markdown
Contributor

@iamnamananand996 iamnamananand996 commented May 12, 2026

Description

Linear ticket: Closes

Fix incorrect deserialization of Java undiscriminated unions where one variant has all-optional fields. Because Jackson's @JsonIgnoreProperties(ignoreUnknown = true) silently accepts any JSON object when every field is optional, an all-optional variant could greedily match a payload intended for a more specific sibling that requires certain keys.

The generated deserializer in UndiscriminatedUnionGenerator now stably partitions non-primitive members so that variants with at least one required wire key (a "key guard") are attempted first, and unguarded (all-optional) variants are tried last. This preserves prior ordering within each partition while ensuring the more specific match wins.

Concrete failure mode this fixes: a union containing Check (requires achHolder) and PayMethodCloud (all optional) previously deserialized {"achHolder": "..."} as PayMethodCloud instead of Check.

Changes Made

  • generators/java/generator-utils/src/main/java/com/fern/java/generators/UndiscriminatedUnionGenerator.java: filter out primitives/boxed primitives, then List#sort non-primitive members so those with required wire keys (getRequiredWireKeys(...) non-empty) come before those without, before emitting their try { ... } catch blocks.
  • generators/java/sdk/changes/unreleased/fix-undiscriminated-union-all-optional-ordering.yml: add a fix changelog entry describing the deserialization fix.
  • Updated README.md generator (if applicable)

Testing

  • Unit tests added/updated
  • Manual testing completed

Link to Devin session: https://app.devin.ai/sessions/2e4e587dd160400390839880a5434932
Requested by: @iamnamananand996


Open in Devin Review

…bers before all-optional ones

All-optional object types (no required fields) were greedily consuming payloads intended
for more specific union members that have at least one required field. The deserializer
now stably sorts members so those with required key guards are emitted first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@devin-ai-integration devin-ai-integration Bot changed the title fix(java): sort undiscriminated union deserializer to try guarded mem… fix(java): emit guarded undiscriminated-union members before all-optional ones in deserializer May 12, 2026
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

@iamnamananand996 iamnamananand996 enabled auto-merge (squash) May 12, 2026 20:47
@github-actions
Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-05-12T05:12:12Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
java-sdk square 212s (n=5) 271s (n=5) 196s -16s (-7.5%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-05-12T05:12:12Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-05-12 20:50 UTC

@iamnamananand996 iamnamananand996 merged commit 880d241 into main May 12, 2026
79 checks passed
@iamnamananand996 iamnamananand996 deleted the fix/java-undiscriminated-union-all-optional-ordering branch May 12, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants