[Bug]: Explicit @Schema(type = "number"|"integer"|"boolean") is emitted as "string" under OpenAPI 3.1

Author: forhadmethunCreated Jul 15, 2026Updated Aug 28, 2026
LabelsBugbacklog

Description of the problem/issue

When generating an OpenAPI 3.1 schema, an explicit @Schema(type = "...") on a property is resolved incorrectly. The scalar type is set correctly on the Schema object, but the types set is populated with ["string"]. Since the 3.1 serializer reads the types set, the property is emitted as type: "string" regardless of the declared type.

  • Goal: produce a correct OpenAPI 3.1 document for DTOs that use explicit @Schema(type = ...).
  • Annotation misbehaving: @Schema — an explicit type = "number" | "integer" | "boolean" is emitted as "string".
  • Correct case: properties relying on type inference (no explicit type in @Schema) serialize correctly. Only an explicitly declared scalar type is affected.
  • Framework: reproduces on plain swagger-core (ModelConverters + Json31); also observed via springdoc-openapi with springdoc.api-docs.version=openapi_3_1.
  • Not examples-related: it reproduces on fields with no example, so it is distinct from #5061 / #5062 (numeric example serialization).
  • Downstream: the resulting spec misreports field types, which breaks OpenAPI-based type/client generation (numbers/booleans generated as string).

Under OpenAPI 3.0 (openapi31 = false) the same DTO serializes correctly, so this is specific to 3.1 generation.

Affected Version

2.2.47

Earliest version the bug appears in (if known): Not fully bisected. Confirmed present in 2.2.47 and still present in 2.2.52 (latest release at time of filing).

Steps to Reproduce

  1. Add a DTO with explicit @Schema(type = ...) properties and resolve it in OpenAPI 3.1 mode:

    java
    import io.swagger.v3.core.converter.AnnotatedType;
    import io.swagger.v3.core.converter.ModelConverters;
    import io.swagger.v3.core.converter.ResolvedSchema;
    import io.swagger.v3.core.util.Json31;
    import io.swagger.v3.oas.annotations.media.Schema;
    import java.math.BigDecimal;
    
    public class Repro {
      enum Freq { DAY, WEEK, MONTH }
    
      static class Dto {
        @Schema(title = "Inferred") public BigDecimal inferred;         // no explicit type (control)
        @Schema(title = "Amount", type = "number")  public BigDecimal amount;
        @Schema(title = "Count",  type = "integer") public Integer count;
        @Schema(title = "Flag",   type = "boolean") public Boolean flag;
        @Schema(title = "Unit")   public Freq unit;                     // enum (control)
      }
    
      public static void main(String[] args) {
        ModelConverters converters = new ModelConverters(true); // openapi31 = true
        ResolvedSchema resolved =
            converters.resolveAsResolvedSchema(new AnnotatedType(Dto.class));
    
        // root cause: scalar type is correct, but the "types" set is ["string"]
        resolved.schema.getProperties().forEach((name, s) ->
            System.out.println(name + " -> getType()=" + s.getType() + " getTypes()=" + s.getTypes()));
    
        System.out.println(Json31.pretty(resolved.schema));
      }
    }
  2. Resolve with new ModelConverters(true) (OpenAPI 3.1 mode) and serialize with Json31.

  3. Observe the serialized output and inspect the resolved Schema objects.

Expected Behavior

The declared type is preserved:

json
{
  "properties": {
    "inferred": { "type": "number" },
    "amount":   { "type": "number" },
    "count":    { "type": "integer" },
    "flag":     { "type": "boolean" },
    "unit":     { "type": "string", "enum": ["DAY", "WEEK", "MONTH"] }
  }
}

Actual Behavior

Every property with an explicit scalar type collapses to "string":

jsonc
{
  "properties": {
    "inferred": { "type": "number" },                                  // OK (inferred, control)
    "amount":   { "type": "string" },                                  // WRONG, declared "number"
    "count":    { "type": "string" },                                  // WRONG, declared "integer"
    "flag":     { "type": "string" },                                  // WRONG, declared "boolean"
    "unit":     { "type": "string", "enum": ["DAY", "WEEK", "MONTH"] }  // OK (enum, control)
  }
}

inferred (inferred type) and unit (enum) serialize correctly — the defect is limited to an explicitly declared scalar type.

Inspecting the resolved (pre-serialization) Schema objects shows the root cause — the scalar type is right, the types set is wrong:

amount -> getType()="number"  getTypes()=["string"]
count  -> getType()="integer" getTypes()=["string"]
flag   -> getType()="boolean" getTypes()=["string"]

Logs / Stack Traces

No exception or log output — the schema is generated silently with the wrong type.

Additional Context

  • The same DTO serialized under OpenAPI 3.0 (new ModelConverters(false)) produces correct types, confirming this is a 3.1-generation regression.
  • Likely related to #4679 (@Schema type/implementation ignored under OpenAPI 3.1).
  • Distinct from #5061 / #5062, which concern numeric example serialization; this issue is about the property type and reproduces with no example present.
  • Current workaround: a post-processing pass that re-syncs each schema's types set to its scalar type produces a correct 3.1 document.

Checklist

  • I have searched the existing issues and this is not a duplicate.
  • I have provided sufficient information for maintainers to reproduce the issue.

Repro.java

Source: swagger-api/swagger-core