Skip to content

Combine const and enum errors into a single message#155

Open
ShivaGupta-14 wants to merge 1 commit intohyperjump-io:mainfrom
ShivaGupta-14:132-combine-const-enum-handlers
Open

Combine const and enum errors into a single message#155
ShivaGupta-14 wants to merge 1 commit intohyperjump-io:mainfrom
ShivaGupta-14:132-combine-const-enum-handlers

Conversation

@ShivaGupta-14
Copy link
Contributor

Description

When multiple const or enum constraints fail (e.g., via allOf), produce a single message with the most restrictive constraint instead of multiple redundant messages.

  • Compute the intersection of allowed values across all constraints
  • If intersection has 1 value: use const message
  • If intersection has multiple values: use enum message
  • If intersection is empty (contradictory): use boolean schema message
  • Fixes: Combine const and enum handlers #132

Checklist

  • npm test
  • npm run lint
  • npm run type-check

@ShivaGupta-14 ShivaGupta-14 force-pushed the 132-combine-const-enum-handlers branch from 7646a94 to ac73647 Compare January 30, 2026 18:24
Copy link
Collaborator

@jdesrosiers jdesrosiers left a comment

Choose a reason for hiding this comment

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

This approach isn't going to work. Try,

{
  "allOf": [
    { "const": "a" },
    { "const": "b" }
  ]
}

@ShivaGupta-14 ShivaGupta-14 force-pushed the 132-combine-const-enum-handlers branch from ac73647 to 3b4eaf2 Compare January 31, 2026 15:51
@ShivaGupta-14
Copy link
Contributor Author

ShivaGupta-14 commented Jan 31, 2026

This approach isn't going to work. Try,

{
  "allOf": [
    { "const": "a" },
    { "const": "b" }
  ]
}

Hi @jdesrosiers,

thanks for pointing out the issue! I understood the problem:

Issue: with a schema like above one -> allOf: [{ const: "a" }, { const: "b" }], if the instance is "a"

  • const: "a" passes (not in errors)
  • Only const: "b" fails
  • My original code only saw failing constraints -> couldn't detect the contradiction
  • My Solution: I updated the handler to collect ALL constraints (both passing and failing), then compute the intersection:
const passed = normalizedErrors[...][schemaLocation] === true;
if (!passed) hasFailure = true;
// always collect
constraints.push({ allowedValues: [...], schemaLocation });

now:

  • Both ["a"] and ["b"] are collected
  • Intersection = [] (empty)
  • Returns getBooleanSchemaErrorMessage()

Tests Added:

  • contradictory const - empty intersection (instance matches one const)
  • contradictory enum - empty intersection (disjoint enum sets)

All tests pass. Is this approach good, or does it need further improvements?
Thanks!

Copy link
Collaborator

@jdesrosiers jdesrosiers left a comment

Choose a reason for hiding this comment

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

That should work. Just a couple small things I'd like to see.

@ShivaGupta-14 ShivaGupta-14 force-pushed the 132-combine-const-enum-handlers branch 2 times, most recently from e7b9dec to 219a62a Compare February 2, 2026 13:08
@ShivaGupta-14 ShivaGupta-14 force-pushed the 132-combine-const-enum-handlers branch from 219a62a to 5a31867 Compare February 2, 2026 22:35
Copy link
Collaborator

@jdesrosiers jdesrosiers left a comment

Choose a reason for hiding this comment

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

The logic to find the most constraining keyword felt off to me and I figured out a case where it doesn't work. Try,

{
  "allOf": [
    { "enum": ["a", "b", "c"] },
    { "enum": ["a", "b", "d"] },
    { "enum": ["a", "b", "e"] }
  ]
}

with,

"c"

It says, "Expected one of ⁨"a" or "b"⁩" with schema location /allOf/0/enum, but that's a passing location. If we exclude passing locations, we get a schema location of /allOf/1/enum, which is better, but it seems contradictory because the message says only "a" and "b" are allowed, but the schema location says that "d" is also allowed. That means we need to include all the failing schema locations to make it clear why only "a" and "b" are allowed. I think the only time we can collapse to a single most constraining location is when the effective allowed set matches exactly one of the keywords.

This is a tough one.

@jdesrosiers
Copy link
Collaborator

Another edge case to consider,

{
  "allOf": [
    { "enum": ["a", "b"] },
    { "enum": ["a", "b"] }
  ]
}

with,

"c"

Should one location be selected as the most constraining, or should we return both locations? I think either one is fine.

@ShivaGupta-14 ShivaGupta-14 force-pushed the 132-combine-const-enum-handlers branch from 5a31867 to 0a80648 Compare February 4, 2026 17:22
@ShivaGupta-14
Copy link
Contributor Author

It says, "Expected one of ⁨"a" or "b"⁩" with schema location /allOf/0/enum, but that's a passing location. If we exclude passing locations, we get a schema location of /allOf/1/enum, which is better, but it seems contradictory because the message says only "a" and "b" are allowed, but the schema location says that "d" is also allowed. That means we need to include all the failing schema locations to make it clear why only "a" and "b" are allowed. I think the only time we can collapse to a single most constraining location is when the effective allowed set matches exactly one of the keywords.

Thanks for pointing it out, I missed it. for such cases, I’ve updated the logic.

  • If the intersection matches exactly one constraint's values -> use that single location
  • Otherwise -> include all constraint locations

this will handle edge case: when intersection is ["a","b"] but no single constraint has exactly ["a","b"], we show all locations so users understand why only "a" and "b" are valid.

also, test added for this case: "multiple enums with no exact match"

@ShivaGupta-14
Copy link
Contributor Author

Should one location be selected as the most constraining, or should we return both locations? I think either one is fine.

return one since both are exactly the same in such cases. also, duplicate identical enums in real schemas are likely unintentional, so returning one seems sufficient.

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.

Combine const and enum handlers

2 participants