[BUG][Go] No json tags emitted when a schema has both properties and a schema-level oneOf, silently dropping snake_case fields on decode

Author: henry-hs-choiCreated Sep 9, 2026Updated Sep 9, 2026

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator?
  • Have you tested with the latest master? — tested with the latest release, v7.25.0
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
Description

When a schema declares properties and a schema-level oneOf, the Go generator emits the model struct with no json tags at all. Go's decoder then falls back to matching field names case-insensitively, and that fallback does not ignore underscores — so every property whose wire name contains one is silently dropped on decode, while single-word properties decode fine.

This is worse than a cosmetic tag issue:

  • MarshalJSON is generated from a hand-built ToMap() that keeps the correct snake_case keys, so serialization stays healthy. Only deserialization loses data, and only for some fields.
  • The generated UnmarshalJSON deletes the known keys from AdditionalProperties, so the value is not recoverable there either.
  • Tests that construct the model in Go and assert on its fields never touch the tags, so they stay green while decoding is broken. The failure surfaces only against a real payload.

The oneOf involved need not define variants. In the repro it is a pure co-occurrence constraint — "either first_value or second_value, not both" — which adds no shape and which the Go generator cannot express in the type system either way. It is valid OpenAPI, and it is the natural way to describe a schema that has gained a second mutually exclusive encoding, yet its mere presence costs the schema every tag.

openapi-generator version

7.25.0. Also reproduced on 7.18.0, so this is not a recent regression.

OpenAPI declaration file content
yaml
openapi: 3.0.3
info:
  title: Tag Loss Repro
  version: 1.0.0
paths:
  /thing:
    get:
      operationId: getThing
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Thing'
components:
  schemas:
    Thing:
      type: object
      required: [kind]
      properties:
        kind:
          type: string
        first_value:
          type: array
          items:
            type: number
        second_value:
          type: array
          items:
            type: number
      oneOf:
        - required: [first_value]
          not:
            required: [second_value]
        - required: [second_value]
          not:
            required: [first_value]
Generation details
bash
docker run --rm -v "$PWD:/local" openapitools/openapi-generator-cli:v7.25.0 generate \
  -i /local/spec.yaml -g go -o /local/out \
  --additional-properties="withGoMod=false,packageName=client"

Actual

go
type Thing struct {
	Kind string
	FirstValue []float32
	SecondValue []float32
}

Expected — and exactly what the generator produces from the same spec with the oneOf block removed, which isolates the trigger:

go
type Thing struct {
	Kind string `json:"kind"`
	FirstValue []float32 `json:"first_value,omitempty"`
	SecondValue []float32 `json:"second_value,omitempty"`
}

With the oneOf present, json.Unmarshal of {"kind":"example","first_value":[1,2],"second_value":[3,4]} leaves both slices empty while Kind is populated.

Related

#21461 reports missing omitempty on the members of a pure oneOf union. Possibly the same code path classifying any schema carrying oneOf as a union model, but the symptom and blast radius differ: that one loses an omitempty on a wrapper, this one loses every tag on an ordinary model and drops data.

Source: OpenAPITools/openapi-generator