regex format checker crashes with ValueError on conflicting inline flags #1558
Description
Activity
I put together a fix for this (this repo restricts PRs to collaborators, so I can't open one directly — happy to if invited, otherwise a maintainer is welcome to cherry-pick this).
Branch: https://github.com/SamyakJ05/jsonschema/tree/fix-regex-format-exception-handling
Widens the
regexchecker's declaredraisesto cover all the non-re.errorexceptionsre.compilecan throw, per the "worth widening whatis_regexcatches" note above:--- a/jsonschema/_format.py +++ b/jsonschema/_format.py @@ -413,7 +413,10 @@ with suppress(ImportError): return is_datetime("1970-01-01T" + instance) -@_checks_drafts(name="regex", raises=re.error) +@_checks_drafts( + name="regex", + raises=(re.error, ValueError, RecursionError, OverflowError), +) def is_regex(instance: object) -> bool: if not isinstance(instance, str): return True
Plus a regression test class covering the reported cases (conflicting inline flags, deep nesting, oversized repeat count) and a sanity check that valid patterns still conform:
--- a/jsonschema/tests/test_format.py +++ b/jsonschema/tests/test_format.py @@ -89,3 +89,33 @@ class TestFormatChecker(TestCase): repr(checker), "<FormatChecker checkers=['bar', 'baz', 'foo']>", ) + + +class TestRegexFormat(TestCase): + """ + ``re.compile`` can raise more than just ``re.error``; the ``regex`` + checker should report all of them as invalid instances rather than + letting them escape as unhandled exceptions. + """ + + def test_unterminated_pattern_is_invalid(self): + checker = FormatChecker() + self.assertFalse(checker.conforms("[unterminated", "regex")) + + def test_conflicting_inline_flags_is_invalid(self): + checker = FormatChecker() + self.assertFalse(checker.conforms("(?u)(?a)", "regex")) + self.assertFalse(checker.conforms("(?a)(?u)", "regex")) + + def test_deeply_nested_pattern_is_invalid(self): + checker = FormatChecker() + pattern = "(" * 100000 + ")" * 100000 + self.assertFalse(checker.conforms(pattern, "regex")) + + def test_oversized_repeat_count_is_invalid(self): + checker = FormatChecker() + self.assertFalse(checker.conforms("(a{100000000000})", "regex")) + + def test_valid_pattern_still_conforms(self): + checker = FormatChecker() + self.assertTrue(checker.conforms(r"^\d+\$", "regex"))
Full test suite passes: 8288 passed, 232 skipped.
- added a commit that references this issue
on Sep 10, 2026 I pushed a focused fix on https://github.com/GruffElixir/jsonschema/tree/fix/regex-format-exceptions. The
regexchecker now treats the extra exceptions raised byre.compileas invalid patterns, with regression coverage for conflicting flags, deep nesting, oversized repeats, and a valid pattern.This repo currently only allows collaborators to open pull requests, so I couldn't create the upstream PR from this account. The branch is ready for a maintainer to pick up or cherry-pick.
The
regexformat checker is registered withraises=re.error:FormatChecker.checkonly converts the declared exception into aFormatError. Butre.compileraisesValueError(not anre.errorsubclass) when a pattern sets two incompatible inline flags, so the exception escapes uncaught and the format check crashes instead of reporting the string as an invalid regex.Reproduction
Also reproduces with the flags in the other order (
"(?a)(?u)"). Note that"(?ua)"- both flags in a single group - returnsFalsecorrectly, so this is specific to the two-group form.Expected
"(?u)(?a)"is not a valid regular expression, so the instance should be reported as invalid, exactly like"[unterminated"is.Cause
ValueErroris not a subclass ofre.error:so
raises=re.errordoes not cover it.Related
Same root cause as #1526 (
OverflowErroron an oversized repeat count) and #1538 (RecursionErroron deep nesting) - three exception types, one declaration that only catchesre.error.There is a fourth case worth mentioning because it shows the set is not stable across CPython versions:
"(?(1)a|b)"(a conditional) raised an uncaughtRuntimeErroron Python 3.9.6, but on 3.13re.compileraisesre.errorfor it and it is now handled correctly. So which exception types escape depends on the CPython release, not just on jsonschema.Given three open issues with one cause, it may be worth widening what
is_regexcatches rather than adding types one at a time -re.compiledoes not documentre.erroras the only exception it can raise.Versions
Reproduced on: