{"record":{"id":"7d1982d97ba51800","repo":"aio-libs/aiohttp","slug":"bad-pattern-pattern-exc","errorCode":null,"errorMessage":"Bad pattern '{pattern}': {exc}","messagePattern":"Bad pattern '(.+?)': (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_urldispatcher.py","lineNumber":435,"sourceCode":"                continue\n\n            match = self.DYN_WITH_RE.fullmatch(part)\n            if match:\n                pattern += \"(?P<{var}>{re})\".format(**match.groupdict())\n                formatter += \"{\" + match.group(\"var\") + \"}\"\n                continue\n\n            if \"{\" in part or \"}\" in part:\n                raise ValueError(f\"Invalid path '{path}'['{part}']\")\n\n            part = _requote_path(part)\n            formatter += part\n            pattern += re.escape(part)\n\n        try:\n            compiled = re.compile(pattern)\n        except re.error as exc:\n            raise ValueError(f\"Bad pattern '{pattern}': {exc}\") from None\n        assert compiled.pattern.startswith(PATH_SEP)\n        assert formatter.startswith(\"/\")\n        self._pattern = compiled\n        self._formatter = formatter\n\n    @property\n    def canonical(self) -> str:\n        return self._formatter\n\n    def add_prefix(self, prefix: str) -> None:\n        assert prefix.startswith(\"/\")\n        assert not prefix.endswith(\"/\")\n        assert len(prefix) > 1\n        self._pattern = re.compile(re.escape(prefix) + self._pattern.pattern)\n        self._formatter = prefix + self._formatter\n\n    def _match(self, path: str) -> dict[str, str] | None:\n        match = self._pattern.fullmatch(path)","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/web_urldispatcher.py#L417-L453","documentation":"After assembling a regex pattern from the path template, aiohttp attempts re.compile(). If the user-supplied regex inside a {var:regex} variable is invalid Python regex, re.error is raised and aiohttp wraps it as ValueError with the offending pattern and the underlying error. This surfaces regex syntax errors in dynamic route definitions at registration time rather than at match time.","triggerScenarios":"Registering a route like app.router.add_get('/users/{id:\\d+(}', handler) where the custom regex '\\d+(' is malformed (unbalanced group). Any invalid regex construct — stray quantifiers, unbalanced parentheses, invalid escapes — inside a {name:regex} segment triggers this.","commonSituations":"Writing a custom regex for path validation with a syntax error; copy-pasting regex from a different regex flavor that Python rejects; overly clever regex that fails to compile; forgetting that the braces themselves are delimiters and the inner pattern must be valid on its own.","solutions":["Test the inner regex in isolation with re.compile before putting it in the route: e.g. re.compile(r'\\d+') should succeed.","Fix the regex syntax (balance parentheses, use valid escapes).","Simplify the regex or validate in the handler instead of the route template."],"exampleFix":"// before\napp.router.add_get('/users/{id:\\d+(}', handler)  # unbalanced group\n// after\napp.router.add_get('/users/{id:\\d+}', handler)   # valid regex","handlingStrategy":"validation","validationCode":"import re\n\ndef validate_route_regex(var_regex: str) -> None:\n    try:\n        re.compile(var_regex)\n    except re.error as e:\n        raise ValueError(f'Invalid route regex {var_regex!r}: {e}') from e","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Test custom regex with re.compile() in isolation before embedding it in a route template.","Keep route regexes simple; prefer validating in the handler for complex rules."],"tags":["routing","regex","path-template","validation"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}