question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Inlining schemas with json-schema-ref-parser is unnecessary, and may not work in case inlined schemas have $id

See original GitHub issue

I think this might be a V7 bug as my schemas were all passing with V6.

If an external schema is referenced using $ref by more than one property, or within a property defined in the definitions section, and the $refs are resolved by putting the schemas inline, as with json-schema-ref-parser, then compilation fails with the error

reference "https://schemas.example.com/samples/child" resolves to more than one schema

What version of Ajv are you using? Does the issue happen if you use the latest version? 7.0.4

Ajv options object {}

JSON Schema

samples/parent.schema.json

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://schemas.example.com/samples/parent",
  "title": "Schema that references another schema multiple times",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "eldestChild": {
      "$ref": "./child.schema.json"
    },
    "youngestChild": {
      "$ref": "./child.schema.json"
    }
  }
}

samples/child.schema.json

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://schemas.example.com/samples/child",
  "title": "Schema referenced from a parent",
  "description": "Example description",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    }
  }
}

It also happens if the $ref only appears once in the file but it’s inside an object in the definitions section:

samples/parent2.schema.json

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://schemas.example.com/samples/parent2",
  "title": "Schema that references another schema",
  "description": "Example description",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "children": {
      "type": "array",
      "items": {
        "$ref": "#/definitions/child"
      }
    }
  },
  "definitions": {
    "child": {
      "type": "object",
      "properties": {
        "order": {
          "type": "number"
        },
        "details": {
          "$ref": "./child.schema.json"
        }
      }
    }
  }
}

This seems to be because traverse() finds it multiple times.

Your code

const Ajv    = require('ajv').default
const $RefParser = require('json-schema-ref-parser')

const ajv = new Ajv();

$RefParser.dereference("samples/parent.schema.json", function(err, schema) {
  if (err) throw err
  try {
    ajv.compile(schema);
    console.log('[ PASS ] Schema validation passed.')
  }
  catch (e) {
    console.log(`[ FAIL ] Schema validation failed: "${e.message}"]`)
    process.exit(1)
  }
})

Validation result, data AFTER validation, error messages

[ FAIL ] Schema validation failed: "reference "https://schemas.example.com/samples/child" resolves to more than one schema"]

What results did you expect? Schemas pass compilation. It’s perfectly valid to reference an external schema multiple times or from inside local definitions, and it used to work fine.

Are you going to resolve the issue? Maybe, but I’m not quite sure where to start. It seems like it’s not necessary to throw an error if a schema is referenced more than once, since it didn’t used to. But I assume that code was added for a reason, and I don’t know what that reason is, or if anything bad would happen if it were removed.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:3
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
epoberezkincommented, Feb 7, 2021

The problem here is that after you use json-schema-ref-parser, you replace two identical refs with two different schema objects, that have the same $id (which is exactly what the error says).

The difference with Ajv v6 is that v7 changed from comparing serialized schemas (and they had to be stable-stringified in v6, not just JSON.serialize). Because of that what v6 saw as identical schemas, v7 sees as different schemas (because it compares them by reference).

While this change does cause some confusion - see #1413 for example - it also highlights the cases when Ajv is used incorrectly (e.g. using ref resolution outside of Ajv as in your example - see below the working approach - or not reusing compiled schemas, and instead at best serialising the schema every time validation is needed as in #1413 or even worse - compiling them every time).

The motivation for this change was:

  • avoiding expensive schema serialization when a custom serialised has to be used (to ensure the same order of object members).
  • reducing memory consumption - it is substantial for large schemas.
  • availability of Map in all browsers that can use schema object itself as a key.

For your particular case, you do not need to use json-schema-ref-parser, instead you need to change $refs in schemas to use schema $ids (absolute, or relative, but not file paths) and add both schemas to Ajv - it should correctly do reference resolution:

const Ajv = require("ajv").default
const ajv = new Ajv({
  schemas: [
    require("./samples/parent.schema.json"),
    require("./samples/child.schema.json"),
  ]
})
const validate = ajv.getSchema("https://schemas.example.com/samples/parent")

You could also use addSchema method to add them not via options:

const ajv = new Ajv()
ajv.addSchema(require("./samples/parent.schema.json"))
ajv.addSchema(require("./samples/child.schema.json"))

Parent schema has to be changed in this way (using relatively URI in $ref):

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://schemas.example.com/samples/parent",
  "title": "Schema that references another schema multiple times",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "eldestChild": {
      "$ref": "child" // absolute URI can also be used here: https://schemas.example.com/samples/child
    },
    "youngestChild": {
      "$ref": "child"
    }
  }
}

This is working repl: https://runkit.com/esp/6020572c572324001a6a3e55

While Ajv does not inline the schema, it still, when possible, inlines the code - as in this case. This behaviour can be changed using the option inlineRefs.

If for some other usage you need a resolved schema you can still use json-schema-ref-parser, as Ajv does not produce it (in general case, it is not possible, because of recursion)

If for some other reason you do need a resolved schema AND it has to be the same schema that you pass to Ajv (although I cannot think why it may be required) then you need to preprocess the schema to make sure all $ids are unique (or they can be simply removed from the inlined fragments - they are not needed there.

Not sure what happened with the parent2 example, when I inline clild inside parent2 it compiles: https://runkit.com/esp/6020565602c3d0001aa8e771. It is possible that you have both parent and parent2 compiled by the same Ajv instance in which case child IDs would conflict for the same reason.

0reactions
harveyconnorcommented, Jun 27, 2022

We are also getting this issue, and struggling to find any answers…

Read more comments on GitHub >

github_iconTop Results From Across the Web

json-schema-ref-parser - UNPKG
The CDN for json-schema-ref-parser. ... node_modules/js-yaml/lib/js-yaml/type/js/function.js", ... n * This method does not resolve any JSON references.
Read more >
JSON Schema $Ref Parser - API Dev Tools
JSON Schema $Ref Parser is a full JSON Reference and JSON Pointer implementation that crawls even the most complex JSON Schemas and gives...
Read more >
Data Models and Schemas - EBSI Specifications -
JSON Schema is a vocabulary that allows you to annotate and validate JSON documents. Since they are machine-readable artifacts, schemas are ...
Read more >
express-openapi-validator - npm
Determines how JSON schema references are resolved by the internal json-schema-ref-parser. Generally, the default mode, bundle is sufficient, ...
Read more >
JSON BinPack: A space-efficient schema-driven and schema ...
7.4.1 Q1: How do JSON-compatible schema-less binary serialization ... tions are not constrained by a text encoding. ... Numeric Non-Redundant Nested.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found