{"id":"71edf431c0cdd815","repo":"aio-libs/aiohttp","slug":"invalid-path-path-part","errorCode":null,"errorMessage":"Invalid path '{path}'['{part}']","messagePattern":"Invalid path '(.+?)'\\['(.+?)'\\]","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_urldispatcher.py","lineNumber":426,"sourceCode":"        super().__init__(name=name)\n        self._orig_path = path\n        pattern = \"\"\n        formatter = \"\"\n        for part in ROUTE_RE.split(path):\n            match = self.DYN.fullmatch(part)\n            if match:\n                pattern += \"(?P<{}>{})\".format(match.group(\"var\"), self.GOOD)\n                formatter += \"{\" + match.group(\"var\") + \"}\"\n                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","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_urldispatcher.py#L408-L444","documentation":"Raised as ValueError by DynamicResource.__init__ when a path segment contains an unmatched '{' or '}' that is not part of a valid variable expression. The path is split by ROUTE_RE and each part is matched against {var} or {var:regex}; any leftover brace is rejected because it cannot be compiled into a meaningful route pattern.","triggerScenarios":"Calling app.router.add_get('/users/{id', handler) (unclosed brace), '/users/}', '/items/{size}/{', or a path with literal braces not expressed as a variable. Also '/a{b' where the brace group is malformed.","commonSituations":"Typos in dynamic path templates; copy-paste leaving a dangling brace; intending a literal brace in a URL (must be handled differently); constructing paths by string interpolation that injects stray braces.","solutions":["Balance all braces: '/users/{id}' with matching opening and closing.","For a variable with a custom regex use '/items/{id:[0-9]+}'.","Avoid literal braces in URLs; if unavoidable, URL-encode them and use a static prefix.","Validate path templates with a regex check before add_get if generated dynamically."],"exampleFix":"# before\napp.router.add_get('/users/{id', get_user)\n\n# after\napp.router.add_get('/users/{id}', get_user)","handlingStrategy":"validation","validationCode":"import re\n_BRACE_OK = re.compile(r'^(?:[^{}]*|\\{[_a-zA-Z][_a-zA-Z0-9]*(?::[^{}]+)?\\})*$')\n\ndef validate_path(path: str) -> str:\n    if not _BRACE_OK.fullmatch(path):\n        raise ValueError(f\"Invalid path template (unmatched braces): {path!r}\")\n    return path","typeGuard":"import re\n_valid = re.compile(r'^(?:[^{}]*|\\{[_a-zA-Z][_a-zA-Z0-9]*(?::[^{}]+)?\\})*$')\n\ndef is_valid_path(path: str) -> bool:\n    return isinstance(path, str) and bool(_valid.fullmatch(path))","tryCatchPattern":"try:\n    app.router.add_get(path, handler)\nexcept ValueError as e:\n    raise ValueError(f\"Bad route path {path!r}: {e}\") from e","preventionTips":["Use a path-template linter or test that compiles all routes on startup.","Avoid building path strings by string formatting that can inject braces.","Pair every '{' with a matching '}'."],"tags":["aiohttp","routing","path","value-error","registration"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}