Skip to content

fix: deserialize Java records when Multi-Release classes are unavailable - #2131

Open
arimu1 wants to merge 1 commit into
square:masterfrom
arimu1:fix/2117-records-shade-multirelease
Open

fix: deserialize Java records when Multi-Release classes are unavailable#2131
arimu1 wants to merge 1 commit into
square:masterfrom
arimu1:fix/2117-records-shade-multirelease

Conversation

@arimu1

@arimu1 arimu1 commented Aug 5, 2026

Copy link
Copy Markdown

Summary

Fixes #2117AssertionError when deserializing Java records after packaging with maven-shade-plugin.

Root cause

Moshi ships RecordJsonAdapter as a Multi-Release JAR:

  • Base (RecordJsonAdapter): stub whose factory always returned null
  • META-INF/versions/16/: real record support

maven-shade-plugin commonly rewrites the jar manifest (e.g. only setting Main-Class) and drops Multi-Release: true, while still copying META-INF/versions/16/. Without that manifest flag the JVM ignores the versioned classes and loads the stub. The factory returns null, Moshi falls through to ClassJsonAdapter, and field assignment on a record fails with IllegalAccessException → bare AssertionError.

Confirmed: the same shaded jar works once Multi-Release: true is restored on the manifest.

Fix

Implement record support in the base RecordJsonAdapter using reflective access to Class.isRecord() / getRecordComponents() (and related APIs). That keeps --release 8 compilation while making records work on JDK 16+ even when Multi-Release classes are not selected.

The typed implementation under src/main/java16 is unchanged and remains preferred when Multi-Release packaging is intact.

Test plan

  • ./gradlew :moshi:records-tests:test (includes new simpleRecordFromJson)
  • ./gradlew :moshi:test
  • ./gradlew spotlessCheck
  • Local shade repro (Temurin 21, shade 3.6.2, no Multi-Release in fat-jar manifest):
    • 1.15.2: AssertionError at ClassJsonAdapter.fromJson
    • this change (2.0.0-SNAPSHOT via mavenLocal): prints Foo[bar=baz]

Notes

Preserving Multi-Release: true in shade config remains a good practice (selects the typed JDK 16 implementation), but is no longer required for record deserialization to work.

maven-shade-plugin (and similar fat-jar tools) often rewrite the jar
manifest and drop Multi-Release: true while leaving META-INF/versions/16
in place. The JVM then loads the base RecordJsonAdapter stub, which
returned null and fell through to ClassJsonAdapter — causing AssertionError
for Java records (square#2117).

Implement record support in the base adapter via reflective access to
Class.isRecord / getRecordComponents so records work even without
Multi-Release selection. The typed java16 implementation remains preferred
when Multi-Release is intact.

Fixes square#2117
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.

AssertionError when deserializing to a record when using maven-shade-plugin

1 participant