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.
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
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
Valueis 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
oneOfa versioning hazard and shifts the burden onto coordinated client releases.