Title: v2.8.0 regression: infinite recursion / stack overflow on self-referential allOf+anyOf schemas (side effect of #1904/#1905) · Issue #2542 · oapi-codegen/oapi-codegen · GitHub
Open Graph Title: v2.8.0 regression: infinite recursion / stack overflow on self-referential allOf+anyOf schemas (side effect of #1904/#1905) · Issue #2542 · oapi-codegen/oapi-codegen
X Title: v2.8.0 regression: infinite recursion / stack overflow on self-referential allOf+anyOf schemas (side effect of #1904/#1905) · Issue #2542 · oapi-codegen/oapi-codegen
Description: Description oapi-codegen v2.8.0 introduced an infinite recursion / stack overflow regression when generating types for a self-referential schema where the recursive $ref sits inside an allOf, and the referenced schema itself has a top-le...
Open Graph Description: Description oapi-codegen v2.8.0 introduced an infinite recursion / stack overflow regression when generating types for a self-referential schema where the recursive $ref sits inside an allOf, and t...
X Description: Description oapi-codegen v2.8.0 introduced an infinite recursion / stack overflow regression when generating types for a self-referential schema where the recursive $ref sits inside an allOf, and t...
Opengraph URL: https://github.com/oapi-codegen/oapi-codegen/issues/2542
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"v2.8.0 regression: infinite recursion / stack overflow on self-referential allOf+anyOf schemas (side effect of #1904/#1905)","articleBody":"## Description\n\n`oapi-codegen` v2.8.0 introduced an infinite recursion / stack overflow regression when generating types for a self-referential schema where the recursive `$ref` sits inside an `allOf`, and the referenced schema itself has a top-level `anyOf`/`oneOf`.\n\nThis appears to be a side effect of the fix for #1905 (PR #1904). That fix makes `mergeOpenapiSchemas` propagate a merged schema's `oneOf`/`anyOf` members up to the caller instead of silently dropping them (which is correct — dropping them was a real bug). However, unlike the sibling `allOf` merge path, which tracks visited `$ref`s via `seenSchemaRef` to guard against cycles, the new `oneOf`/`anyOf` propagation added by #1904 has **no cycle detection at all**. When the propagated `anyOf`/`oneOf` transitively re-enters the same `allOf` merge, generation recurses forever instead of terminating or erroring.\n\n## Minimal reproduction\n\n`spec.yaml`:\n```yaml\nopenapi: 3.0.0\ninfo:\n title: repro\n version: \"1.0.0\"\npaths: {}\ncomponents:\n schemas:\n Node:\n anyOf:\n - type: object\n properties:\n leaf:\n type: string\n - type: object\n properties:\n children:\n type: array\n items:\n allOf:\n - $ref: '#/components/schemas/Node'\n - type: object\n properties:\n extra:\n type: string\n```\n\n`cfg.yaml`:\n```yaml\npackage: repro\ngenerate:\n models: true\noutput: out.gen.go\n```\n\n```\ngo run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml spec.yaml\n```\n\n### Result on v2.8.0\n\nImmediate stack overflow:\n\n```\nruntime: goroutine stack exceeds 1000000000-byte limit\nfatal error: stack overflow\n\ngoroutine 1 [running]:\ngithub.com/oapi-codegen/oapi-codegen/v2/pkg/codegen.mergeSchemas(...)\n\tpkg/codegen/merge_schemas.go:24\ngithub.com/oapi-codegen/oapi-codegen/v2/pkg/codegen.MergeSchemas(...)\n\tpkg/codegen/merge_schemas.go:21\ngithub.com/oapi-codegen/oapi-codegen/v2/pkg/codegen.GenerateGoSchema(...)\n\tpkg/codegen/schema.go:852\ngithub.com/oapi-codegen/oapi-codegen/v2/pkg/codegen.oapiSchemaToGoType(...)\n\tpkg/codegen/schema.go:1233\ngithub.com/oapi-codegen/oapi-codegen/v2/pkg/codegen.GenerateGoSchema(...)\n\tpkg/codegen/schema.go:1210\ngithub.com/oapi-codegen/oapi-codegen/v2/pkg/codegen.GenerateGoSchema(...)\n\tpkg/codegen/schema.go:1005\ngithub.com/oapi-codegen/oapi-codegen/v2/pkg/codegen.generateUnion(...)\n\tpkg/codegen/schema.go:1629\ngithub.com/oapi-codegen/oapi-codegen/v2/pkg/codegen.GenerateGoSchema(...)\n\tpkg/codegen/schema.go:1051\ngithub.com/oapi-codegen/oapi-codegen/v2/pkg/codegen.mergeSchemas(...)\n\tpkg/codegen/merge_schemas.go:112\ngithub.com/oapi-codegen/oapi-codegen/v2/pkg/codegen.MergeSchemas(...)\n\tpkg/codegen/merge_schemas.go:21\n... (repeats identically until the goroutine stack limit is hit)\n```\n\n### Result on v2.7.2 (before #1904)\n\nGeneration succeeds, but silently drops the recursive `$ref` from the `allOf` — the exact data-loss bug #1905/#1904 set out to fix:\n\n```go\n// Node1 defines model for .\ntype Node1 struct {\n\tChildren *[]struct {\n\t\tExtra *string `json:\"extra,omitempty\"`\n\t} `json:\"children,omitempty\"`\n}\n```\n\nNote `Node1.Children` items have no reference back to `Node` at all — the recursive member of the `allOf` was dropped entirely.\n\n## Where we hit this\n\nWe hit this generating a Go client from Kibana's OpenAPI spec, on a real recursive schema shaped exactly like the repro (`Kibana_HTTP_APIs_ContentPackIncludedObjects`, a content-pack \"included objects\" tree with a `routing` array whose items are `allOf: [$ref: self, {destination}]`). Upgrading `oapi-codegen` from v2.7.2 to v2.8.0 turned a silent correctness bug (dropped recursive ref) into a hard crash.\n\n## Suggested fix\n\nExtend the same `seenSchemaRef` cycle-guard already used for `allOf` members in `mergeAllOf`/`mergeSchemas` to the `oneOf`/`anyOf` propagation added in `mergeOpenapiSchemas` (PR #1904), so a schema that re-enters an `allOf` merge it's already inside of is skipped/short-circuited instead of re-expanded indefinitely.\n\n## Environment\n\n- `oapi-codegen` v2.8.0 (regression vs v2.7.2)\n- Go 1.26.1, darwin/arm64 (also reproduces regardless of platform — this is pure recursion, not a goroutine-stack-size issue)\n","author":{"url":"https://github.com/tobio","@type":"Person","name":"tobio"},"datePublished":"2026-08-26T07:53:08Z","url":"https://github.com/oapi-codegen/oapi-codegen/issues/2542"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:7d53c6ba-5f21-2fac-a44b-0bd91852ae16 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | EDB2:32A4B7:760DA6:A50C6B:6AF9CD53 |
| html-safe-nonce | 4113dfc6e181d8a8c3cef5e02f76484267cacb5b94ee809a9d96ee5124c31712 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJFREIyOjMyQTRCNzo3NjBEQTY6QTUwQzZCOjZBRjlDRDUzIiwidmlzaXRvcl9pZCI6IjU5MDM2MDM1NTg1NjQ2MTM0NTkiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | cfce92c5c5b24ef28e55b845e41b6de38a34d848db917776e18bc8991943403f |
| hovercard-subject-tag | issue:5254664131 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/oapi-codegen/oapi-codegen/2542/issue_layout |
| twitter:image | https://opengraph.githubassets.com/f144ba24ffb9e1f380aefae74417663daace30e61f64552954864a25bb83fd15/oapi-codegen/oapi-codegen/issues/2542 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/f144ba24ffb9e1f380aefae74417663daace30e61f64552954864a25bb83fd15/oapi-codegen/oapi-codegen/issues/2542 |
| og:image:alt | Description oapi-codegen v2.8.0 introduced an infinite recursion / stack overflow regression when generating types for a self-referential schema where the recursive $ref sits inside an allOf, and t... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | tobio |
| hostname | github.com |
| expected-hostname | github.com |
| None | 612d3e6ffd693e7ea8d9fcf452370995872baef31d825b1fedef704cd3d24ac7 |
| turbo-cache-control | no-preview |
| go-import | github.com/oapi-codegen/oapi-codegen git https://github.com/oapi-codegen/oapi-codegen.git |
| octolytics-dimension-user_id | 142752710 |
| octolytics-dimension-user_login | oapi-codegen |
| octolytics-dimension-repository_id | 173009358 |
| octolytics-dimension-repository_nwo | oapi-codegen/oapi-codegen |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 173009358 |
| octolytics-dimension-repository_network_root_nwo | oapi-codegen/oapi-codegen |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 072e34035c13c31543fba9f025cbbbda4210dae4 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width