Skip to content

OpenAPI 3.1: schemas with const but no type lose type information #2567

Open

Description

@g-logunov

A schema containing only const, without an explicit type, generates an alias to any. This affects all constants: scalars, arrays and objects.

The reproduction below compares scalar const schemas with their explicitly typed equivalents.

Reproduced against main at commit 833a968f84b4b0ec7c2f3fb5bb119e3c449f7a4f (v2.8.1-0.20260924063949-833a968f84b4). The scalar reproduction below also fails on the latest release, v2.8.0.

Save as spec.yaml:

openapi: 3.1.0
info:
  title: Scalar const inference reproduction
  version: "1"
paths: {}
components:
  schemas:
    StringConst:
      const: active
    TypedStringConst:
      type: string
      const: active

    IntegerConst:
      const: 0
    TypedIntegerConst:
      type: integer
      const: 0

    BooleanConst:
      const: false
    TypedBooleanConst:
      type: boolean
      const: false

    NumberConst:
      const: 1.5
    TypedNumberConst:
      type: number
      const: 1.5

Save as config.yaml:

package: repro
output: types.gen.go
generate:
  models: true
output-options:
  skip-prune: true

skip-prune: true retains the unreferenced schemas. Other settings use their defaults.

Run in a directory containing those two files:

go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@833a968f84b4b0ec7c2f3fb5bb119e3c449f7a4f \
  --config=config.yaml spec.yaml

Generation succeeds, but produces different representations for each equivalent pair:

Constant Without explicit type With explicit type
active type StringConst = any type TypedStringConst string
0 type IntegerConst = any type TypedIntegerConst int
false type BooleanConst = any type TypedBooleanConst bool
1.5 type NumberConst = any type TypedNumberConst float32

Only the explicitly typed schemas receive enum constants and Valid() methods.

On v2.8.0, the untyped schemas generate interface{} instead of any; the typed controls behave as above.

Additional checks with the scalar reproduction on the pinned main commit:

  • Explicit compatibility.schema-merging-behavior: v1, v2, and experimental v3: same result as the default configuration.
  • output-options.skip-enum-via-oneof: true: same result.
  • output-options.skip-enum-validate: true: untyped schemas still generate any; the typed controls retain their scalar types and constants, with Valid() methods disabled as expected.
  • Setting output-options.type-mapping.number.default.type: float64 changes TypedNumberConst to float64, while NumberConst remains any.
  • Changing the document version to openapi: 3.1.1: same result.

Expected: scalar const schemas should receive the same typing and singleton-enum support as their explicitly typed counterparts, respecting configured Go type mappings.

Non-scalar constraints may not be expressible through Go types alone, but scalars already have supported representations.

Under OpenAPI 3.1's JSON Schema semantics, const requires equality with the specified value. Adding type to the scalar examples above does not change which values they accept. OpenAPI Schema Object, JSON Schema const.

The same behavior occurs inside object properties: a property containing only const: active becomes an any field.

In generateGoSchema, a missing type reaches the generic-schema branch, which selects any and returns before the scalar const/enum handler. Generic-schema branch, const/enum handler.

Related: #2520 added inference for the oneOf + title + const pattern. This report concerns standalone const schemas without type and reproduces after that change.

The reproduction is provided under Apache-2.0.

Activity

  1. mromaszewicz commented on Sep 26, 2026

    @mromaszewicz
    Member

    I'm not sure how to fix this reliably. Untyped constants and enums which map to any work in terms of serialization and deserialization, but what breaks is parameter binding in the runtime package. I can fix that by simply passing through the string and assigning that to the any.

    One thing I considered is to infer the data type of the const/enum from its values, and that's where it gets annoying. Say you have a typeless enum with values [3, "4", "false", 9.7]`. What is the correct type? I realize that's contrived, but it's an example of what I hit.

    Even with numbers, it's a little tricky, if I have [3, 4.2], then I pick float, which can represent both, but then, when someone puts a number greated than 2^53 in there, JSON can't marshal it without precision loss, and it would have been better for it to be a string.

    I think users should provide types here, and I think current behavior of using any is ok. I guess if the Valid() method is important, we could create a new type, still backed by any and validate values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions