Skip to content

Allow schema to still validate even if AJV fails#1269

Merged
datho7561 merged 1 commit into
redhat-developer:mainfrom
Alphadelta14:metaschema-can-still-validate
Jun 5, 2026
Merged

Allow schema to still validate even if AJV fails#1269
datho7561 merged 1 commit into
redhat-developer:mainfrom
Alphadelta14:metaschema-can-still-validate

Conversation

@Alphadelta14
Copy link
Copy Markdown
Contributor

@Alphadelta14 Alphadelta14 commented Jun 3, 2026

I was trying to track down yet another StackOverflow error in AJV that wasn't fixed in #1260. But honestly, I would like to see this code be slightly more resilient to AJV since having a schema that isn't valid against the metaschema still is possible to do a lot of checking that is very valuable.

What does this PR do?

This captures a nasty RangeError (Maximum call stack size exceeded) that is possible in AJV when you have multiple local $ref's recursively referencing each other (while their $id has a full uri).
In that case, it just warns to the console and allows normal validation to continue. (note that having a schema resolution error causes the file to FULL STOP processing and no more validation hints are given).

What issues does this PR fix or reference?

If a file has a relative # yaml-language-server: $schema=./... and that schema has a $ref: "./path-b.schema.yaml" (with a full $id: "https://example.com/my/path-b.yaml") which may refer to a $ref: "./path-c.schema.yaml" (with a full $id: "https://example.com/my/path-c.yaml") that may refer back to path-a, AJV can end up being confused and cause infinite recursion.

Is it tested? How?

Multi-step recursion tested in vscode-yaml:

Details

cat > example.yaml <<'EXAMPLE'
# yaml-language-server: \$schema=./example.schema.yaml
# Here is my simple file
description: my top level description
blocks:
- text: some content lives here
EXAMPLE

cat > example.schema.yaml <<'EXSCHEMA'
# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema
$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "https://example.com/my/example.schema.yaml"
title: "My Example Schema"
description: "The root schema"
type: object
allOf:
  - $ref: "./top.schema.yaml"
EXSCHEMA

cat > top.schema.yaml <<'TOP'
# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema
$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "https://example.com/my/example.schema.yaml"
title: "Top-level items of the schema"
description: "Top-level items of the schema"
type: object
properties:
  description:
    type: string
    description: Human readable summary
  blocks:
    description: List of blocks
    type: array
    items:
      oneOf:
        - $ref: "./text.schema.yaml"
        - $ref: "./numerical.schema.yaml"
        - $ref: "./mixed.schema.yaml"
unevaluatedProperties: false
TOP

cat > text.schema.yaml <<'TEXT'
# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema
$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "https://example.com/my/text.schema.yaml"
title: "Text items"
description: "Text item blocks"
type: object
properties:
  text:
    type: string
required: [text]
additionalProperties: false
TEXT

cat > numerical.schema.yaml <<'NUM'
# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema
$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "https://example.com/my/numerical.schema.yaml"
title: "Numerical items"
description: "Numerical item blocks"
type: object
properties:
  number:
    type: number
  complex:
    type: string
additionalProperties: false
NUM

cat > mixed.schema.yaml <<'MIXED'
# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema
$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "https://example.com/my/numerical.schema.yaml"
title: "Mixed items"
description: "Mixed item blocks"
type: object
properties:
  multi:
    $ref: "#/$defs/multiItems"
required: [multi]
additionalProperties: false

$defs:
  multiItems:
    type: array
    items:
      oneOf:
        - $ref: "./text.schema.yaml"
        - $ref: "./numerical.schema.yaml"
        - $ref: "#/$defs/multiItems"
MIXED

So now you open up example.yaml and this particular case works well the first time. But if you comment out - $ref: "./text.schema.yaml", check that example.yaml goes to invalid, then uncomment it, it would have stopped working, but with my change, it now continues to work with a logged:

Schema '/path/to/test-schemas/example.schema.yaml' could not be fully validated: Maximum call stack size exceeded

@Alphadelta14 Alphadelta14 requested a review from datho7561 as a code owner June 3, 2026 23:34
AJV can throw a RangeError (max recursion) when trying to resolve the metaschema
even if the schema is valid
@Alphadelta14 Alphadelta14 force-pushed the metaschema-can-still-validate branch from 4dd1ea5 to 93ae44d Compare June 3, 2026 23:35
valid = validator(toValidate);
} catch (e) {
// AJV overflows on recursive/cyclic schemas; attempt to degrade gracefully
console.warn(l10n.t("Schema '{0}' could not be fully validated: {1}", loc, e.message));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be better to report this as a warning or error on the text document and provide translations for this message, but I think this is good for now.

Copy link
Copy Markdown
Contributor

@datho7561 datho7561 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works well to fix the bug. Thanks, Heather!

@datho7561 datho7561 added the bug label Jun 5, 2026
@datho7561 datho7561 added this to the 1.24.0 milestone Jun 5, 2026
@datho7561 datho7561 merged commit 3409d5d into redhat-developer:main Jun 5, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants