Skip to content

A oneOf/anyOf branch that is a whole-file $ref generates Go that doesn't parse #2639

Open

Description

@BogdanYarotsky

When a oneOf or anyOf branch is a $ref to a whole file ($ref: ./user.yaml, no # fragment), the union's As*/From*/Merge* accessors are named after the branch's inline Go type, so the generated code has methods like AsStruct {\n Name *string ... }() and formatting fails.

Whole-file refs are otherwise generated inline (IsGoTypeReference returns false for them, #308): the same file used directly as a body schema or as a property works, and an inline branch (- {type: object, properties: ...}) in the same union gets its own type, GetItems200JSONResponseBody0.

Reproduction

openapi.yaml:

openapi: 3.0.3
info: {title: t, version: "1"}
paths:
  /items:
    get:
      responses:
        "200":
          description: ok
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: ./user.yaml
                  - $ref: ./group.yaml

user.yaml:

type: object
properties:
  name: {type: string}

group.yaml:

type: object
properties:
  id: {type: integer}
$ oapi-codegen -generate types -package api -o api.gen.go openapi.yaml
error generating code: error formatting Go code at line 53: api.go:53:5: expected declaration, found Name (and 2 more errors)

No configuration file. The unformatted output (with output-options: skip-fmt: true) shows where it breaks:

        // AsStruct {
    Name *string`json:"name,omitempty"`
} returns the union data inside the GetItems200JSONResponseBody as a struct {
    Name *string`json:"name,omitempty"`
}
        func (t GetItems200JSONResponseBody) AsStruct {
    Name *string`json:"name,omitempty"`
}() (struct {

The same happens with anyOf, with only one branch being a whole-file ref, with openapi: 3.1.0, for a property's oneOf, and for a component schema (components.schemas.Item: {oneOf: [$ref: ./user.yaml, $ref: ./group.yaml]}, referenced from the response). The component form fails the same way on v2.4.1 through v2.6.0; the inline body form has failed since v2.7.0, when inline body unions started getting accessors.

Expected

The branches are generated like inline ones: GetItems200JSONResponseBody0 and GetItems200JSONResponseBody1, with AsGetItems200JSONResponseBody0() and so on, and the code compiles. Or, if whole-file refs aren't meant to be supported as union branches, an error that says so.

Versions

v2.8.1 and main (15f5714, v2.8.1-0.20261028140811-15f571472ff8, error at line 56 there), Go 1.27.3, linux/amd64.

Likely cause

generateUnionElement (pkg/codegen/union.go:88 on main) names and defines a branch only when element.Ref == "". A whole-file ref has a non-empty Ref, but generateGoSchema inlined it, so its GoType is the struct literal. Checking !IsGoTypeReference(element.Ref) there instead makes both forms above generate the ...0/...1 types, and the output compiles; I haven't run the test suite on that change.

Activity

  1. n-hiraha commented on Oct 30, 2026

    @n-hiraha

    I reproduced this on main (15f5714) on darwin/arm64 with the exact files above (same error, at line 56).

    The change you suggested in generateUnionElement, checking !IsGoTypeReference(element.Ref) instead of element.Ref == "", fixes it for me. The output has GetItems200JSONResponseBody0 / GetItems200JSONResponseBody1 with AsGetItems200JSONResponseBody0() etc., the generated package passes go vet, and go test ./pkg/codegen/... passes.

    I haven't run the full suite yet (the internal/test module and make generate).

    Are you planning to open a PR for this? If not, I'd be happy to put one up with a regression test covering the response-body, property, and component-schema forms you listed, and credit you for the diagnosis.

  2. BogdanYarotsky commented on Oct 30, 2026

    @BogdanYarotsky
    Author

    Hi @n-hiraha, please, go ahead with the PR. I am fine with it 🙂 Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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