Describe the bug
In graphql-java 26, SimplePerformantInstrumentation is annotated with @NullMarked. Its unannotated InstrumentationState callback parameters are therefore exposed to Kotlin as non-null.
However, createState() and createStateAsync() remain explicitly nullable, and the default SimplePerformantInstrumentation.createState() implementation returns null.
This appears to be a nullability-contract mismatch introduced by the JSpecify instrumentation changes in #4272.
The callback state parameters should either be annotated @Nullable, matching the existing optional-state behavior, or graphql-java should guarantee a non-null empty state. Making the parameters nullable would preserve the existing runtime contract.
To Reproduce
Please provide a code example or even better a test to reproduce the bug.
import graphql.ExecutionResult
import graphql.GraphQL
import graphql.execution.instrumentation.InstrumentationContext
import graphql.execution.instrumentation.InstrumentationState
import graphql.execution.instrumentation.SimplePerformantInstrumentation
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters
import graphql.schema.idl.RuntimeWiring
import graphql.schema.idl.SchemaGenerator
import graphql.schema.idl.SchemaParser
import kotlin.test.Test
class InstrumentationStateNullabilityTest {
private class StatelessInstrumentation : SimplePerformantInstrumentation() {
override fun beginExecution(
parameters: InstrumentationExecutionParameters,
state: InstrumentationState
): InstrumentationContext<ExecutionResult>? = null
}
@Test
fun `stateless Kotlin instrumentation should execute without an NPE`() {
val typeRegistry = SchemaParser().parse(
"""
type Query {
hello: String
}
""".trimIndent()
)
val schema = SchemaGenerator().makeExecutableSchema(
typeRegistry,
RuntimeWiring.MOCKED_WIRING
)
val graphQL = GraphQL.newGraphQL(schema)
.instrumentation(StatelessInstrumentation())
.build()
graphQL.execute("{ hello }")
}
}
The execution fails when beginExecution is invoked:
java.lang.NullPointerException:
Parameter specified as non-null is null:
method StatelessInstrumentation.beginExecution, parameter state
A current workaround is to return a non-null marker state:
private object EmptyInstrumentationState : InstrumentationState
override fun createState(
parameters: InstrumentationCreateStateParameters
): InstrumentationState = EmptyInstrumentationState
Describe the bug
In graphql-java 26,
SimplePerformantInstrumentationis annotated with@NullMarked. Its unannotatedInstrumentationStatecallback parameters are therefore exposed to Kotlin as non-null.However,
createState()andcreateStateAsync()remain explicitly nullable, and the defaultSimplePerformantInstrumentation.createState()implementation returnsnull.This appears to be a nullability-contract mismatch introduced by the JSpecify instrumentation changes in #4272.
The callback state parameters should either be annotated
@Nullable, matching the existing optional-state behavior, or graphql-java should guarantee a non-null empty state. Making the parameters nullable would preserve the existing runtime contract.To Reproduce
Please provide a code example or even better a test to reproduce the bug.
The execution fails when
beginExecutionis invoked:A current workaround is to return a non-null marker state: