Skip to content

fix: honour JSpecify @Nullable on controller parameters - #3378

Merged
bnasslahsen merged 1 commit into
springdoc:mainfrom
sharanggupta:gh-3377-jspecify-nullable-request-param
Oct 23, 2026
Merged

bnasslahsen merged 1 commit into
springdoc:mainfrom
sharanggupta:gh-3377-jspecify-nullable-request-param

Conversation

@sharanggupta

@sharanggupta sharanggupta commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A JSpecify @Nullable (TYPE_USE only, the annotation Spring Framework 7 standardises on) declared on a controller method parameter is dropped before schema extraction. Spring already marks the parameter required: false, but its schema stays non-nullable, unlike the same parameter annotated with Spring's declaration-targeted @Nullable (app102). #3300 fixed this for flattened @ParameterObject fields; this applies the same treatment to direct parameters.

Change Plan

  • GenericParameterService.resolveTypeAndTypeAnnotationsForParameter: forward the annotations on the parameter's own AnnotatedType as well as those on its type arguments, by reusing annotationsFromAnnotatedType (made null-safe for parameters whose AnnotatedType cannot be resolved).
  • New webmvc regression app app274 (OAS 3.0 and 3.1) with @RequestParam @Nullable String / Integer using org.jspecify.annotations.Nullable; expected ["string","null"] / nullable: true. The same app has a @PathVariable @Nullable String id endpoint whose golden file shows the path parameter stays required: true and non-nullable, because SpringDocUtils.fixNullablePathParameter (nullable: true dropped for @Nullable @PathVariable parameters when using springdoc.api-docs.version=openapi-3-0 (regression in 3.1.1) #3358) still applies after this change.
  • CHANGELOG entry under Unreleased.

Annotations that target both PARAMETER and TYPE_USE (for example @Size) now appear in the merged annotation array for direct parameters the same way they already do for @ParameterObject fields since #3300; the existing suites (including app270's List<@Pattern String>) are unchanged.

Test Plan

SpringDocApp274Test fails on main (schema.type Expected: a JSON array got: string / Expected: nullable but none found) and passes with the fix. springdoc-openapi-starter-common (55 tests) and springdoc-openapi-starter-webmvc-api (655 tests) suites are green.

Fixes #3377

For a controller method parameter, GenericParameterService only forwarded
the annotations declared on the parameter's type arguments to swagger-core,
never the ones declared on the parameter type itself. A type-use-only
annotation such as the JSpecify @nullable (the annotation Spring Framework 7
standardises on) was therefore dropped before schema extraction: Spring
already marked the parameter as not required, but its schema stayed
non-nullable, unlike the same parameter annotated with Spring's declaration
targeted @nullable.

Flattened @ParameterObject fields were given both sets of annotations in
springdoc#3300. Apply the same treatment to direct parameters by reusing
annotationsFromAnnotatedType, made null-safe for the parameters whose
AnnotatedType cannot be resolved.

The regression app also covers a @PathVariable carrying the same
annotation, which the springdoc#3358 guard keeps non-nullable.

Fixes springdoc#3377
@sharanggupta
sharanggupta force-pushed the gh-3377-jspecify-nullable-request-param branch from 2ee61c2 to ada02a0 Compare October 6, 2026 20:58
@bnasslahsen
bnasslahsen merged commit 676ae86 into springdoc:main Oct 23, 2026
bnasslahsen pushed a commit that referenced this pull request Oct 23, 2026
A JSpecify @nullable, which only targets type uses, declared on a
controller method parameter was dropped before schema extraction, so the
parameter schema stayed non-nullable. Read the annotations of the
parameter's own annotated type as well as the ones of its type
arguments. JSpecify is added as a test dependency, as Spring Framework 6
does not bring it.

Backport of gh-3378, fixes gh-3377

Signed-off-by: Badr NASS LAHSEN <badr.nasslahsen@gmail.com>
@bnasslahsen

Copy link
Copy Markdown
Collaborator

Thanks @sharanggupta! Merged, with the tests moved to app278 since app274 is now taken on main, and backported to spring-boot-3 (1a2c2d5), where JSpecify is added as a test dependency.

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.

JSpecify @Nullable on a controller method parameter is not reflected as nullable in the parameter schema

2 participants