Skip to content

fix: apply sibling keywords when a schema resolves a $ref - #867

Open
lazerg wants to merge 1 commit into
fastify:mainfrom
lazerg:fix/ref-sibling-keywords
Open

fix: apply sibling keywords when a schema resolves a $ref#867
lazerg wants to merge 1 commit into
fastify:mainfrom
lazerg:fix/ref-sibling-keywords

Conversation

@lazerg

@lazerg lazerg commented Aug 8, 2026

Copy link
Copy Markdown

Problem

A property that both references another schema and adds its own keywords loses those keywords. resolveRef swaps the whole location for the reference target, so anything sitting next to $ref is gone before any code is generated:

const stringify = build({
  type: 'object',
  properties: {
    x: { $ref: 'Foo#', required: ['extra'] }
  }
}, {
  schema: {
    Foo: {
      $id: 'Foo',
      type: 'object',
      properties: { value: { type: 'integer' }, extra: { type: 'string' } },
      required: ['value']
    }
  }
})

stringify({ x: { value: 1 } })
// ACTUAL:   {"x":{"value":1}}
// EXPECTED: throws '"extra" is required!'

value is enforced because it comes from Foo, extra is not, so incomplete data serializes as if it were valid.

Fix

A $ref that carries siblings now gets the resolved target and those siblings merged through mergeLocations, the same path allOf already takes. Everything else keeps the direct dereference it always had:

  • siblings that cannot change the output (title, description, $comment, examples, deprecated, readOnly, writeOnly) are skipped, so annotated refs still share one serializer
  • when the sibling keywords and the target genuinely conflict (say a sibling type the target contradicts) the merge is dropped and the plain target is used, which is what happens today

Merged refs are cached by content rather than by object identity, since a merge clones its input and a recursive $ref would otherwise be merged again on every level.

Test

Two cases in test/ref.test.js, internal and external $ref with a sibling required: the sibling constraint is enforced and the target's own required still is. Both fail on the current main.

Fixes #866

@lazerg
lazerg force-pushed the fix/ref-sibling-keywords branch from 2b5d0bb to ed8b59a Compare August 8, 2026 05:55
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.

Sibling required keyword is dropped when a property uses $ref to an external schema

1 participant