OpenAPITools / OpenAPITools/openapi-generator
[BUG][Spring] useJspecify=true can emit @Nullable without importing org.jspecify.annotations.Nullable
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Description
When using the spring generator with useJspecify=true, generated API interfaces can contain @Nullable in parameter types/signatures without the required import:
import org.jspecify.annotations.Nullable;
This causes compilation failures in generated sources.
Confirmed affected case
Confirmed with:
- generator:
spring - library:
spring-cloud - options:
useJspecify=trueopenApiNullable=falseuseSpringBoot3=true
Generated output example
Generated Spring API interfaces can contain method parameters such as:
ResponseEntity<APIProxyDeploymentDetailsEnv> getAPIProxyDeploymentEnv(
@Parameter(name = "org_name", description = "Organization name.", required = true, in = ParameterIn.PATH)
@PathVariable("org_name")
@Nullable String orgName,
@Parameter(name = "env_name", description = "Environment name.", required = true, in = ParameterIn.PATH)
@PathVariable("env_name")
String envName,
@Parameter(name = "api_name", description = "API proxy name.", required = true, in = ParameterIn.PATH)
@PathVariable("api_name")
String apiName
);
but the generated imports do not include:
import org.jspecify.annotations.Nullable;
This causes compilation errors like:
error: cannot find symbol
symbol: class Nullable
Upstream source inspection
While investigating the generator source, this appears to be in the shared Spring parameter/import path rather than in any project-specific template customization.
Relevant files in this repository:
modules/openapi-generator/src/main/resources/JavaSpring/pathParams.mustachemodules/openapi-generator/src/main/resources/JavaSpring/queryParams.mustachemodules/openapi-generator/src/main/resources/JavaSpring/headerParams.mustachemodules/openapi-generator/src/main/resources/JavaSpring/bodyParams.mustachemodules/openapi-generator/src/main/resources/JavaSpring/cookieParams.mustachemodules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation.mustachemodules/openapi-generator/src/main/resources/JavaSpring/optionalDataType.mustachemodules/openapi-generator/src/main/resources/JavaSpring/api.mustachemodules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/api.mustachemodules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.javamodules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java
In particular:
SpringCodegensets the mapping:
importMapping.put("Nullable", useJspecify ? "org.jspecify.annotations.Nullable" : "org.springframework.lang.Nullable");
AbstractJavaCodegen.applyJspecify()also mapsNullabletoorg.jspecify.annotations.Nullable.AbstractJavaCodegen.addNullableImportForOperation(...)adds theNullableimport only when a parameter matchesnotRequiredOrIsNullable().AbstractJavaCodegen.JSpecifyNullableLambda/jSpecifyDatatypeare involved in moving@Nullableinto the type position.
That means there is at least one Spring generation path where @Nullable reaches the rendered signature but codegenOperation.imports does not receive Nullable.
Scope / likely affected variants
This is confirmed for spring + library=spring-cloud.
From source inspection, the same shared Spring parameter partials are reused by other Spring generator variants, so it is worth validating at least:
spring+library=spring-bootspring+library=spring-http-interface- delegate/interface generation paths using
JavaSpring/api.mustache
I am not claiming all of those are confirmed broken, only that they appear to share the same template path and should be checked.
Expected behavior
Whenever @Nullable is emitted and useJspecify=true, the generated file should also include:
import org.jspecify.annotations.Nullable;
Actual behavior
@Nullable appears in generated Spring API signatures, but the corresponding import is sometimes missing.
Workaround
As a workaround, I post-process generated files and inject the missing import whenever @Nullable is present but the import is missing:
// OAG spring-cloud template emits @Nullable on required object-type parameters but
// doesn't always add the import when useJspecify=true (known generator bug).
// Post-process to ensure the import is present wherever @Nullable is used.
fileTree(layout.buildDirectory.dir("generated-code")).matching {
include '**/*.java'
}.each { file ->
def text = file.text
if (text.contains('@Nullable')
&& !text.contains('import org.jspecify.annotations.Nullable;')) {
file.text = text.replaceFirst(
'(package [^;]+;\\s*\\n)',
'$1import org.jspecify.annotations.Nullable;\n'
)
}
}
Version
Observed with:
- OpenAPI Generator:
7.22.0
Suggested fix
Please review the Spring generator import registration for JSpecify-enabled parameter rendering and ensure that every code path that can emit @Nullable also registers Nullable in the operation/class imports.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Trace AbstractJavaCodegen.addNullableImportForOperation and the JSpecify rendering used by SpringCodegen, then compare the shared parameter partials and API templates listed in the issue. Reproduce the confirmed spring-cloud generation with useJspecify=true and verify the generated Java compiles with the Nullable import; check the other named Spring variants for the same result.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, openapi, spring
- Domain
- backend-api-design, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100