Found during the review of #2522; pre-existing on main and independent of that PR.
Problem
MergeSchemas (pkg/codegen/merge_schemas.go) only errors on a type conflict when both members declare a type:
if s1.Type.Slice() != nil && s2.Type.Slice() != nil && !equalTypes(s1.Type, s2.Type) {
return openapi3.Schema{}, fmt.Errorf("can not merge incompatible types: ...")
}
result.Type = s1.Type
When s1 has no type and s2 does, result.Type = s1.Type silently discards s2's type. Because allOf members are merged pairwise in order, the generated Go shape depends on member ordering.
Reproduction
AllOfUnionMember:
allOf:
- properties:
name:
type: string
- type: [string, number]
generates a plain struct — the second member's type constraint vanishes entirely:
type AllOfUnionMember struct {
Name *string `json:"name,omitempty"`
}
Reversing the member order flips which constraint silently wins: result.Type then carries the union, the schema lowers to any, and the properties are dropped instead. The same mechanism applies to single types (- type: string as the second member is discarded the same way); the multi-type union case from #2521/#2522 just made it easier to notice, and converted some of these from hard generation errors into silent, order-dependent output.
Expected
Order should not change the result. Either:
- propagate the type when exactly one member declares one (result.Type = firstNonNil(s1.Type, s2.Type)), keeping the existing incompatibility error when both declare different types, or
- error loudly on the typeless+typed combination if propagation has unwanted interactions (e.g. a propagated primitive type alongside merged properties still silently drops the properties at the dispatch stage — propagation alone doesn't make that combination coherent).
The first option fixes the common case (a typeless member contributing only properties/required alongside a typed member); the second at least removes the silent order dependency. Behavior change to generated code either way, so this likely wants a look at what existing specs in the wild rely on before choosing.
Found during the review of #2522; pre-existing on main and independent of that PR.
Problem
MergeSchemas (pkg/codegen/merge_schemas.go) only errors on a type conflict when both members declare a type:
When s1 has no type and s2 does, result.Type = s1.Type silently discards s2's type. Because allOf members are merged pairwise in order, the generated Go shape depends on member ordering.
Reproduction
generates a plain struct — the second member's type constraint vanishes entirely:
Reversing the member order flips which constraint silently wins: result.Type then carries the union, the schema lowers to any, and the properties are dropped instead. The same mechanism applies to single types (- type: string as the second member is discarded the same way); the multi-type union case from #2521/#2522 just made it easier to notice, and converted some of these from hard generation errors into silent, order-dependent output.
Expected
Order should not change the result. Either:
The first option fixes the common case (a typeless member contributing only properties/required alongside a typed member); the second at least removes the silent order dependency. Behavior change to generated code either way, so this likely wants a look at what existing specs in the wild rely on before choosing.