Skip to content

OneOf inhabitability fixes - #4458

Open
jbellenger wants to merge 4 commits into
graphql-java:masterfrom
jbellenger:jbellenger-oneof-inhabitability-2
Open

OneOf inhabitability fixes#4458
jbellenger wants to merge 4 commits into
graphql-java:masterfrom
jbellenger:jbellenger-oneof-inhabitability-2

Conversation

@jbellenger

@jbellenger jbellenger commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

OneOf input objects can be defined in a way that makes it impossible to create a value for that type. This breaks the spec's unwritten requirement that all input types be inhabited.

The simplest example of an uninhabited OneOf is the self-recursing input Foo @oneOf { foo:Foo }. Trying to create a value for this type requires an infinitely nested object value, and should be invalid for the same reason that the spec currently considers the non-OneOf variant input Foo { foo:Foo! } to be invalid.

I have an open spec PR that updates the recursive input object requirements to consider OneOf input objects. The proposed algorithm was implemented in graphql-js here.

I previously added a version of these rules to graphql-java, but this was before I had a complete grasp on the problem and the validation is incomplete.

For example, this mixed-OneOf type graph also requires infinite values and is incorrectly allowed by graphql-java:

input Foo @oneOf { bar:Bar }
input Bar { foo:Foo! }

This PR closes this validation hole by porting over graphql-js's implementation of this validator.

Perf

The validator runs once per schema. It uses a worklist algorithm and takes time that is linear relative to the total number of input object fields in a schema. I've included a benchmark that tests a variety of different schemas:

Benchmark                                                           Mode  Cnt   Score   Error  Units
InputObjectHasUnbreakableCycleBenchmark.finiteValuePropagation      avgt    6  33.829 ± 5.185  us/op
InputObjectHasUnbreakableCycleBenchmark.tenTypeMixedInputCycle      avgt    6  34.151 ± 4.320  us/op
InputObjectHasUnbreakableCycleBenchmark.tenTypeTenFieldsInputCycle  avgt    6  52.809 ± 8.401  us/op
InputObjectHasUnbreakableCycleBenchmark.twoTypeMixedInputCycle      avgt    6  28.624 ± 3.921  us/op

The takeaway is that performance is relatively stable even for pathological input object graphs.

…inhabitability-2

# Conflicts:
#	src/main/java/graphql/schema/validation/NoUnbrokenInputCycles.java
#	src/main/java/graphql/schema/validation/SchemaValidator.java
#	src/test/groovy/graphql/schema/validation/NoUnbrokenInputCyclesTest.groovy
#	src/test/groovy/graphql/schema/validation/SchemaValidatorTest.groovy
@github-actions

Copy link
Copy Markdown
Contributor

Test Report

Test Results

Java Version Total Passed Failed Errors Skipped
Java 11 5957 (+17 🟢) 5901 (+17 🟢) 0 (±0) 0 (±0) 56 (±0)
Java 17 5957 (+17 🟢) 5900 (+17 🟢) 0 (±0) 0 (±0) 57 (±0)
Java 21 5957 (+17 🟢) 5900 (+17 🟢) 0 (±0) 0 (±0) 57 (±0)
Java 25 5957 (+17 🟢) 5900 (+17 🟢) 0 (±0) 0 (±0) 57 (±0)
jcstress 32 (±0) 32 (±0) 0 (±0) 0 (±0) 0 (±0)
Total 23860 (+68 🟢) 23633 (+68 🟢) 0 (±0) 0 (±0) 227 (±0)

Code Coverage (Java 25)

Metric Covered Missed Coverage vs Master
Lines 30139 3122 90.6% ±0.0%
Branches 8808 1520 85.3% ±0.0%
Methods 8043 1206 87.0% ±0.0%

Changed Class Coverage (2 classes)

Class Line Branch Method
g.s.v.InputObjectHasUnbreakableCycle +98.4% 🟢 +94.6% 🟢 +100.0% 🟢
g.s.v.NoUnbrokenInputCycles removed removed removed

Full HTML report: build artifact jacoco-html-report

Updated: 2026-08-23 23:10:06 UTC

@jbellenger jbellenger mentioned this pull request Aug 24, 2026
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.

2 participants