Skip to content

Inline array or property-less object error body generates code that does not compile #61

Description

@giraffesyo

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.

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