Skip to content

[Java][Spring] Place @Valid on Optional/JsonNullable type argument (HV000271) - #25099

Open
jorgerod wants to merge 4 commits into
OpenAPITools:masterfrom
InditexTech:bugfix/GH-25097-hibernate-valid-container
Open

jorgerod wants to merge 4 commits into
OpenAPITools:masterfrom
InditexTech:bugfix/GH-25097-hibernate-valid-container

Conversation

@jorgerod

@jorgerod jorgerod commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #25097. Follow-up to #24176.

#24176 moved @Valid from List/Set/Map containers to their type argument. The Java Spring generator still emits a member- or parameter-level @Valid when a single model is wrapped in Optional (useOptional) or JsonNullable (openApiNullable). Hibernate Validator 9.1+ treats both wrappers as containers (JsonNullable through the ValueExtractor registered by jackson-databind-nullable), so this still logs HV000271.

Changes

Case Before After
Model field/getter, useOptional @Valid Optional<Item> getSingleItem() Optional<@Valid Item> getSingleItem()
Nullable model field/getter, openApiNullable @Valid JsonNullable<Item> getX() JsonNullable<@Valid Item> getX()
Optional query (deepObject) / multipart / body model param @Valid ... Optional<Item> item ... Optional<@Valid Item> item
Optional scalar body @Valid @RequestBody(required = false) Optional<String> body @RequestBody(required = false) Optional<String> body
  • Only models get the type-argument @Valid. Wrapped scalars (Optional<BigDecimal>, JsonNullable<String>…) have nothing to cascade into, so they get none. This matches what fix(java): place bean-validation @Valid on the type argument instead of the container (HV000271) #24176 did for scalar query/form params.
  • Required or unwrapped single objects keep their member/parameter-level @Valid.
  • A new typeUseAnnotations Mustache lambda (AbstractJavaCodegen#placeTypeUseAnnotations) places type-use annotations after the package qualifier of fully qualified types, e.g. JsonNullable<com.example.@Valid ExternalModel>. The form <@Valid com.example.ExternalModel> does not compile (seen with schemaMapping and with Resource/Instant types).

Tests

  • SpringCodegenTest: new tests on 3_0/spring/issue_25097_valid_container.yaml covering the model getters/fields (Optional, JsonNullable, Lombok), optional query/form/body params, FQN types, and regression guards for the cases fixed in fix(java): place bean-validation @Valid on the type argument instead of the container (HV000271) #24176 (list/map params, DTO getters, composed array items, nested maps). Three existing expectations were updated to the new output (Optional<@Valid Zebra>, JsonNullable<com.example.@Valid ExternalModel>).
  • JavaClientCodegenTest: regression guard for container DTO getters (resttemplate/native/webclient).
  • AbstractJavaCodegenTest: unit test for placeTypeUseAnnotations.
  • ParameterAssert#hasAnnotatedType: new assertion that keeps type-use annotations. The existing hasType ignores them.
  • org.openapitools.codegen.java.** passes: 1267 tests, 0 failures.
  • Runtime check: I validated the generated Spring models and API interfaces (ExecutableValidator) with Hibernate Validator 9.1.3.Final, using invalid nested payloads. HV000271 is gone, and nested violations are still reported with no duplicates.

PR checklist


Summary by cubic

Fixes HV000271 by placing @Valid on the type argument of Optional/JsonNullable-wrapped models instead of the member or parameter, so generated code now emits Optional<@Valid Item>.

  • Wrapped models on fields, getters, query/form/body params, and Lombok accessors now carry @Valid on the type argument; wrapped scalars get none, and required or unwrapped models keep their member/parameter-level @Valid.
  • Adds a Mustache lambda that places type-use annotations after the package qualifier of fully qualified types (e.g. JsonNullable<com.example.@Valid ExternalModel>), the only form that compiles.
  • Updates existing expectations and regenerated samples for the new output; all changed sample projects compile.
  • Covers regression guards for container cases from fix(java): place bean-validation @Valid on the type argument instead of the container (HV000271) #24176 (list/map params, DTO getters, composed array items, nested maps).

Written for commit 07b96a7. Summary will update on new commits.

Review in cubic

jorgerod and others added 4 commits October 2, 2026 11:24
… (HV000271)

Follow-up to OpenAPITools#24176. A single model wrapped in Optional (useOptional) or
JsonNullable (openApiNullable) still received a member/parameter-level
@Valid, which Hibernate Validator 9.1+ reports as HV000271 because both
wrappers are containers (JsonNullable via the ValueExtractor registered by
jackson-databind-nullable).

- Model fields/getters: Optional<@Valid Model> / JsonNullable<@Valid Model>
  instead of a member-level @Valid.
- Optional query/form/body parameters: Optional<@Valid Model>; optional
  scalar bodies no longer get a parameter-level @Valid (as OpenAPITools#24176 did for
  scalar query/form params).
- Only models get the type-argument @Valid; wrapped scalars have nothing to
  cascade into.
- New typeUseAnnotations lambda places type-use annotations after the
  package qualifier of fully qualified types
  (JsonNullable<com.example.@Valid ExternalModel>), required to compile.

Adds tests for each case and regenerates affected samples.

Fixes OpenAPITools#25097

This branch has not been deployed

No deployments
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.

[Bug][Java/Spring] Container-level @Valid on Optional/JsonNullable-wrapped models still triggers HV000271 (follow-up to #24176)

1 participant