From f249d48eccc6f938e2ed55424818b9c46568d7a9 Mon Sep 17 00:00:00 2001 From: Diya Date: Fri, 19 Dec 2025 04:17:16 +0530 Subject: [PATCH 1/3] WIP: anyOf Discriminator found but no alt match --- src/error-handlers/anyOf.js | 22 ++++++++++++++++++++-- src/keyword-error-message.test.js | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/error-handlers/anyOf.js b/src/error-handlers/anyOf.js index 7550250..b06cd29 100644 --- a/src/error-handlers/anyOf.js +++ b/src/error-handlers/anyOf.js @@ -151,9 +151,27 @@ const anyOfErrorHandler = async (normalizedErrors, instance, localization) => { } // Discriminator identified, but none of the alternatives match if (discriminatedAlternatives.length === 0) { - // TODO: For now, it will use the schema description strategy - } + const discriminatorLocation = [...discriminator][0]; + + const messageSet = new Set(); + for (const alternative of allAlternatives) { + if (discriminatorLocation in alternative) { + const errorObjects = await getErrors({ [discriminatorLocation]: alternative[discriminatorLocation] }, instance, localization); + for (const errorObject of errorObjects) { + messageSet.add(errorObject.message); + } + } + } + if (messageSet.size > 0) { + errors.push({ + message: [...messageSet].join(" or "), + instanceLocation: discriminatorLocation, + schemaLocation + }); + continue; + } + } // Last resort, select the alternative with the most properties matching the instance const instanceProperties = new Set(Instance.values(instance).map((node) => Instance.uri(node))); let maxMatches = -1; diff --git a/src/keyword-error-message.test.js b/src/keyword-error-message.test.js index 79d56ea..f119781 100644 --- a/src/keyword-error-message.test.js +++ b/src/keyword-error-message.test.js @@ -976,7 +976,7 @@ describe("Error messages", async () => { ]); }); - test.skip("anyOf - discriminator with no matches", async () => { + test("anyOf - discriminator with no matches", async () => { registerSchema({ $schema: "https://json-schema.org/draft/2020-12/schema", anyOf: [ From d4ed452874530a6615710886e9e1f8f83d42a6d6 Mon Sep 17 00:00:00 2001 From: Diya Date: Thu, 1 Jan 2026 04:08:56 +0530 Subject: [PATCH 2/3] add missing error from test output Signed-off-by: Diya --- src/keyword-error-message.test.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/keyword-error-message.test.js b/src/keyword-error-message.test.js index f119781..c45357b 100644 --- a/src/keyword-error-message.test.js +++ b/src/keyword-error-message.test.js @@ -1019,6 +1019,10 @@ describe("Error messages", async () => { absoluteKeywordLocation: `https://example.com/main#/anyOf/1/properties/type/const`, instanceLocation: "#/type" }, + { + absoluteKeywordLocation: `https://example.com/main#/anyOf/1/properties/box/type`, + instanceLocation: "#/box" + }, { absoluteKeywordLocation: `https://example.com/main#/anyOf`, instanceLocation: "#" @@ -1032,7 +1036,10 @@ describe("Error messages", async () => { { schemaLocation: `https://example.com/main#/anyOf`, instanceLocation: "#/type", - message: `Invalid value. Expected "a", "b". Found "d".` + message: localization.getAnyOfBulletsErrorMessage([ + localization.getConstErrorMessage("a"), + localization.getConstErrorMessage("b") + ]) } ]); }); From a9b67a1109848fce399e310192f6f6cec9877311 Mon Sep 17 00:00:00 2001 From: Diya Date: Thu, 1 Jan 2026 04:11:10 +0530 Subject: [PATCH 3/3] remove single discriminator prop assumption Signed-off-by: Diya --- src/error-handlers/anyOf.js | 39 ++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/error-handlers/anyOf.js b/src/error-handlers/anyOf.js index b06cd29..8f87471 100644 --- a/src/error-handlers/anyOf.js +++ b/src/error-handlers/anyOf.js @@ -151,24 +151,35 @@ const anyOfErrorHandler = async (normalizedErrors, instance, localization) => { } // Discriminator identified, but none of the alternatives match if (discriminatedAlternatives.length === 0) { - const discriminatorLocation = [...discriminator][0]; - - const messageSet = new Set(); - for (const alternative of allAlternatives) { - if (discriminatorLocation in alternative) { - const errorObjects = await getErrors({ [discriminatorLocation]: alternative[discriminatorLocation] }, instance, localization); - for (const errorObject of errorObjects) { - messageSet.add(errorObject.message); + let discriminatorErrorFound = false; + for (const discriminatorLocation of discriminator) { + /** @type Set */ + const messageSet = new Set(); + let matchesAny = false; + for (const alternative of allAlternatives) { + if (discriminatorLocation in alternative) { + const errorObjects = await getErrors({ [discriminatorLocation]: alternative[discriminatorLocation] }, instance, localization); + if (errorObjects.length === 0) { + matchesAny = true; + break; + } + for (const errorObject of errorObjects) { + messageSet.add(errorObject.message); + } } } + + if (!matchesAny) { + discriminatorErrorFound = true; + errors.push({ + message: localization.getAnyOfBulletsErrorMessage([...messageSet]), + instanceLocation: discriminatorLocation, + schemaLocation + }); + } } - if (messageSet.size > 0) { - errors.push({ - message: [...messageSet].join(" or "), - instanceLocation: discriminatorLocation, - schemaLocation - }); + if (discriminatorErrorFound) { continue; } }