2026-09-29 · 6 min read · Rahul Chitturi
- json-schema
- validation
- references
A JSON Schema $ref points at another schema, but the keywords sitting next to it do not mean the same thing in every draft. Older drafts treat $ref as a replacement for its siblings. Draft 2019-09 and later apply adjacent keywords as well, so a description, unevaluatedProperties constraint, or allOf sibling can suddenly become real validation.
Failures show up after a bundler inlines references, a registry changes the retrieval base URI, or production runs a different dialect than the editor. The schema text can look unchanged while the evaluated contract is not.
Record the dialect and the resolved target
Save the declared $schema, the exact document that failed, the validator name and version, and whether the schema was bundled before validation. Follow each $ref to its final target and note the base URI used for that lookup.
- Identify whether siblings of $ref are ignored or applied.
- Preserve $id and $anchor values during bundling.
- Separate a missing target from a target that fails validation.
- Compare the editor, CI, and runtime validators.
Watch cycles, bundles, and remote retrieval
A cycle is valid when the instance eventually stops matching it, but a naive recursive inliner can loop or drop a sibling that only the original draft would have applied. Bundlers also rewrite relative references, so a schema that worked as separate files can fail as one document.
Remote $ref retrieval can change with network policy, cache entries, or a registry that serves a newer schema at the same URI. Pin the retrieved bytes in the failing fixture instead of assuming the URL still means yesterday's document.
Compare the schema before and after resolution
Use Flashman's JSON formatter for the source and bundled schemas, diff for validator output, the YAML/JSON converter when the contract lives in config, and the regex tester when a sibling pattern is part of the failure.
Test ignored versus applied siblings, broken and cyclic references, changed base URIs, bundled versus multi-file schemas, anchors, remote cache hits, dialect upgrades, and the same instance in every validator you ship.