Skip to content

An unknown discriminator value fails the whole decode, so adding a oneOf variant breaks existing clients #39

Description

@giraffesyo

A generated discriminated union rejects any variant it does not know (internal/templates/types.go.tmpl:61):

default:
    return fmt.Errorf("unknown {{ .Discriminator.PropertyName }} value: %q", disc.{{ ... }})

So adding a variant to a spec breaks every client build that predates it, at decode time, for responses that are otherwise perfectly usable.

Two things make this sharper than it first looks.

It fails the whole payload, not the unknown element. Unions are commonly list elements. One unrecognized entry in a hundred fails the entire response, so a client cannot process the variants it does understand.

Adding a variant is normally backward compatible. Under an untyped decode a new variant is inert until the client is rebuilt. Anyone treating "add a case to a oneOf" as additive — which is the usual reading — will ship a change that breaks deployed clients with no server-side signal.

Repro

openapi: 3.1.0
info: { title: t, version: "1" }
paths:
  /shapes:
    get:
      operationId: listShapes
      responses:
        "200":
          description: ok
          content:
            application/json:
              schema:
                type: array
                items:
                  title: Shape
                  oneOf:
                    - $ref: "#/components/schemas/Circle"
                    - $ref: "#/components/schemas/Square"
                  discriminator:
                    propertyName: kind
                    mapping:
                      circle: "#/components/schemas/Circle"
                      square: "#/components/schemas/Square"
components:
  schemas:
    Circle:
      type: object
      properties: { kind: { type: string }, radius: { type: number } }
    Square:
      type: object
      properties: { kind: { type: string }, side: { type: number } }

Generate, then decode a response where a third kind has been added server-side:

[{"kind":"circle","radius":1},{"kind":"triangle","base":2,"height":3}]

The call fails with unknown kind value: "triangle". The circle is discarded along with it.

Suggested direction

Preserve the raw payload for an unrecognized variant instead of erroring — keep the bytes in the union wrapper so Value is nil but the data survives a re-marshal, and let callers detect the unknown case explicitly. That keeps a re-marshal lossless and makes adding a variant additive again, which is what spec authors will assume.

If erroring stays the default, it would help to say so in the README, since it makes every oneOf a versioning hazard and shifts the burden onto coordinated client releases.

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