Skip to content

fix: avoid stack overflow for multiple anyOf root refs - #911

Merged
Tony133 merged 2 commits into
fastify:mainfrom
junaidkhan80121:fix/anyof-root-ref-stack-overflow
Nov 8, 2026
Merged

Tony133 merged 2 commits into
fastify:mainfrom
junaidkhan80121:fix/anyof-root-ref-stack-overflow

Conversation

@junaidkhan80121

Copy link
Copy Markdown
Contributor

Summary

Fix a stack overflow when a schema has two or more anyOf/oneOf fields that each include { $ref: '#' } (self-ref to the root). Merging each pure $ref with an empty sibling schema cloned the root and re-entered buildOneOf forever.

Linked issue

Closes #724

Changes

  • When an anyOf/oneOf has no sibling keywords and an option is a pure $ref, build the resolved target schema directly instead of merging
  • Add regression tests for multiple anyOf and oneOf root refs

Validation

  • npm run test:unit — 663 passed, 100% coverage

junaidkhan80121 and others added 2 commits November 7, 2026 18:33
…words

The fast path added for fastify#724 only applies when the anyOf/oneOf has no
sibling keywords and the option is exactly `{ $ref }`. Any sibling keyword
next to the anyOf (a `description`, `nullable`, `properties`...) or on the
option itself still merged the $ref target, which clones it and re-enters
buildOneOf with new option objects that the identity-keyed cache never
recognizes, overflowing the stack again.

Mirror what resolveRef already does: key the merge by content (resolved
$ref + the sibling keywords cloned with absolute $refs) so every clone of
a recursive target reuses the same merged schema. Any option with a $ref
takes this path, since resolveRef handles the option's own siblings.

Regression tests: root refs with sibling keywords, refs to a non-root
definition, sibling keywords on the option, and the same sibling text in
two external schemas (which must not share a merge).
@Tony133
Tony133 force-pushed the fix/anyof-root-ref-stack-overflow branch from 107bbd3 to b0c5ba4 Compare November 7, 2026 17:40
@Tony133

Tony133 commented Nov 7, 2026

Copy link
Copy Markdown
Member

Thanks for the fix! I rebased the branch on main (7.3.2) and pushed a follow-up commit on top of yours.

The fast path only kicked in when the anyOf had no sibling keywords: a description/nullable next to the anyOf (or on the $ref option itself) still overflowed, and that's what TypeBox/OpenAPI-generated schemas emit everywhere. The follow-up memoizes the merge by content, same as resolveRef already does; regression tests added, coverage stays at 100%.

@Tony133
Tony133 merged commit 03ac72d into fastify:main Nov 8, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multiple fields with anyOf and self ref causes Maximum call stack size exceeded

2 participants