Repository navigation
NullPointerException when oneOf changes to anyOf in response schema #381
Description
I observed a NullPointerException when a response schema changes from using oneOf to anyOf. Here is a test that reproduces the issue.
The error is as follows:
java.lang.NullPointerException: Cannot invoke "java.util.List.iterator()" because the return value of "io.swagger.v3.oas.models.media.ComposedSchema.getOneOf()" is null
at org.openapitools.openapidiff.core.compare.schemadiffresult.ComposedSchemaDiffResult.getMapping(ComposedSchemaDiffResult.java:120)
at org.openapitools.openapidiff.core.compare.schemadiffresult.ComposedSchemaDiffResult.diff(ComposedSchemaDiffResult.java:68)
at org.openapitools.openapidiff.core.compare.SchemaDiff.computeDiffForReal(SchemaDiff.java:354)
at org.openapitools.openapidiff.core.compare.SchemaDiff.computeDeferredDiff(SchemaDiff.java:330)
at org.openapitools.openapidiff.core.compare.SchemaDiff.diff(SchemaDiff.java:309)
at org.openapitools.openapidiff.core.compare.SchemaDiff.diff(SchemaDiff.java:301)
at org.openapitools.openapidiff.core.compare.ContentDiff.lambda$diff$1(ContentDiff.java:44)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
at org.openapitools.openapidiff.core.compare.ContentDiff.diff(ContentDiff.java:32)
at org.openapitools.openapidiff.core.compare.ResponseDiff.computeDiff(ResponseDiff.java:60)
at org.openapitools.openapidiff.core.compare.ResponseDiff.computeDiff(ResponseDiff.java:16)
at org.openapitools.openapidiff.core.compare.ReferenceDiffCache.cachedDiff(ReferenceDiffCache.java:52)
at org.openapitools.openapidiff.core.compare.ResponseDiff.diff(ResponseDiff.java:42)
at org.openapitools.openapidiff.core.compare.ApiResponseDiff.diff(ApiResponseDiff.java:35)
at org.openapitools.openapidiff.core.compare.OperationDiff.diff(OperationDiff.java:91)
at org.openapitools.openapidiff.core.compare.PathDiff.diff(PathDiff.java:39)
[...]
The direct reason seems to be that in ComposedSchemaDiffResult#diff there is this check:
if (CollectionUtils.isNotEmpty(leftComposedSchema.getOneOf())
|| CollectionUtils.isNotEmpty(rightComposedSchema.getOneOf())) {
and later, in getMapping(rightComposedSchema) it's assumed that both left and right contain oneOf, which is incorrect as the check here is an OR instead of an AND.
Changing || to && fixes this particular issue and the tests pass, however I'm not certain that this is the correct fix as I don't understand the logic enough to be able to tell if I'm not breaking anything else.
Additionally, the way that anyOf is handled seems to be merging all schema alternatives into one, which includes merging their required options, which seems incorrect. If a schema is anyOf(A, B), where A has required property propA and B has required property propB, after the merge the result would be a schema that requires both propA and propB instead of propA OR propB.
Do you think that the || to && is a valid fix for the issue, or should the anyOf handling be changed somehow?
Activity
bors2908 commentedon Feb 23, 2026
Experienced the same issue:
While comparing two schemas with maven plugin, one file had absent
oneOf, other had it present, so it happily went throughif (CollectionUtils.isNotEmpty(leftComposedSchema.getOneOf()) || CollectionUtils.isNotEmpty(rightComposedSchema.getOneOf())) {And failed with NPE in
org.openapitools.openapidiff.core.compare.schemadiffresult.ComposedSchemaDiffResult#getMapping:for (Schema<?> schema : composedSchema.getOneOf()) {Unexpected error: Cannot invoke "java.util.List.iterator()" because the return value of "io.swagger.v3.oas.models.media.ComposedSchema.getOneOf()" is null
Iterator here has ZERO null-protection. It's funny that issue haven't been fixed for such a long time.
I believe that null check and replacement with an empty list should do the trick, since after this diff will be emptyList vs nonEmptyList
DrSatyr commentedon Feb 23, 2026
Thanks for reporting this.
I think we should scope #381 to the runtime crash only (NPE on
oneOf -> anyOf), and handleanyOfsemantics separately.The
anyOf/allOfbehavior has already been discussed and tracked:allOf/anyOfvsoneOf)Unify Handling of allOf/anyOf and oneOf in Diff logic #772
DrSatyr/issue212 #774
Issue 422 #771
anyOfbug report: [bug] support of breaking changes for "anyOf" #212 (closed)[bug] support of breaking changes for "anyOf" #212
For this issue, the proposed fix is specifically to prevent the NPE in
ComposedSchemaDiffResult#getMappingwhengetOneOf()is null (e.g., right side isanyOf).That resolves the crash without changing current
anyOfmerge semantics.NullPointerException when oneOf changes to anyOf in response schema
DrSatyr commentedon Feb 23, 2026
@bors2908 please review #892
bors2908 commentedon Feb 23, 2026
@DrSatyr
LGTM
NullPointerException when oneOf changes to anyOf in response schema (O…