Skip to content

[BUG][Spring] useJspecify=true can emit @Nullable without importing org.jspecify.annotations.Nullable #23848

@ThalysGomes

Description

@ThalysGomes

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=true
    • openApiNullable=false
    • useSpringBoot3=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.mustache
  • modules/openapi-generator/src/main/resources/JavaSpring/queryParams.mustache
  • modules/openapi-generator/src/main/resources/JavaSpring/headerParams.mustache
  • modules/openapi-generator/src/main/resources/JavaSpring/bodyParams.mustache
  • modules/openapi-generator/src/main/resources/JavaSpring/cookieParams.mustache
  • modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation.mustache
  • modules/openapi-generator/src/main/resources/JavaSpring/optionalDataType.mustache
  • modules/openapi-generator/src/main/resources/JavaSpring/api.mustache
  • modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/api.mustache
  • modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java
  • modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java

In particular:

  • SpringCodegen sets the mapping:
importMapping.put("Nullable", useJspecify ? "org.jspecify.annotations.Nullable" : "org.springframework.lang.Nullable");
  • AbstractJavaCodegen.applyJspecify() also maps Nullable to org.jspecify.annotations.Nullable.
  • AbstractJavaCodegen.addNullableImportForOperation(...) adds the Nullable import only when a parameter matches notRequiredOrIsNullable().
  • AbstractJavaCodegen.JSpecifyNullableLambda / jSpecifyDatatype are involved in moving @Nullable into 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-boot
  • spring + 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions