The generated client is the product and has to compile. It does not when an error response's schema is inline and resolves to a Go type expression rather than a type name.
Repro
openapi: 3.1.0
info: { title: c, version: "1" }
paths:
/t:
get:
operationId: getT
responses:
"200": { description: ok, content: { application/json: { schema: { type: string } } } }
default:
description: fallback
content:
application/json:
schema: { type: object }
$ go run . generate --spec c.yaml --out ./c
Generated 0 types into ./c (package c)
$ cd c && go build ./...
./errors.go:65:6: syntax error: unexpected keyword map, expected name
./errors.go:78:21: syntax error: missing type constraint
./operations.go:17:31: syntax error: unexpected name anyResponse at end of statement
What lands in the output:
// errors.go
type map[string]anyResponse struct {
*APIError
Detail map[string]any
}
func parsemap[string]anyResponse(err error) error {
// operations.go
return nil, parsemap[string]anyResponse(err)
An inline type: array body fails the same way, with []string in the identifier position.
Trigger
The error body's schema has to be inline and resolve to a type expression:
| error body |
result |
type: object with no properties, inline |
broken, becomes map[string]any |
type: array, inline |
broken, becomes []T |
type: object with properties, inline |
fine, gets a named type |
$ref to an array or map alias |
fine, the alias has a name |
$ref to an object schema |
fine |
default: { schema: { type: object } } is a common way to say "some JSON error body", so this is not an exotic shape.
Cause
errorType (internal/generator/funcmap.go:661) and uniqueErrorTypes (internal/generator/funcmap.go:630) return resp.TypeName unchanged, and errors.go.tmpl concatenates it into type <TypeName>Response and func parse<TypeName>Response. Nothing checks that the name is an identifier.
ir.NamedType (internal/ir/types.go:17) already draws exactly this distinction and is not consulted on this path.
Fix
Either skip the typed wrapper when the body has no name to build one from, so the operation falls back to a bare APIError, or give the wrapper a name derived from the operation and status code instead of from the body's type expression. The second keeps the parsed body reachable, which is the point of the wrapper.
Either way the e2e net should grow a case: the existing tests compile generated clients, and this shape is not among them.
This should block the next release.
The generated client is the product and has to compile. It does not when an error response's schema is inline and resolves to a Go type expression rather than a type name.
Repro
What lands in the output:
An inline
type: arraybody fails the same way, with[]stringin the identifier position.Trigger
The error body's schema has to be inline and resolve to a type expression:
type: objectwith no properties, inlinemap[string]anytype: array, inline[]Ttype: objectwith properties, inline$refto an array or map alias$refto an object schemadefault: { schema: { type: object } }is a common way to say "some JSON error body", so this is not an exotic shape.Cause
errorType(internal/generator/funcmap.go:661) anduniqueErrorTypes(internal/generator/funcmap.go:630) returnresp.TypeNameunchanged, and errors.go.tmpl concatenates it intotype <TypeName>Responseandfunc parse<TypeName>Response. Nothing checks that the name is an identifier.ir.NamedType(internal/ir/types.go:17) already draws exactly this distinction and is not consulted on this path.Fix
Either skip the typed wrapper when the body has no name to build one from, so the operation falls back to a bare
APIError, or give the wrapper a name derived from the operation and status code instead of from the body's type expression. The second keeps the parsed body reachable, which is the point of the wrapper.Either way the e2e net should grow a case: the existing tests compile generated clients, and this shape is not among them.
This should block the next release.