aio-libs/aiohttp · error · ValueError
Invalid path '{path}'['{part}']
Error message
Invalid path '{path}'['{part}'] What it means
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.
Source
Thrown at aiohttp/web_urldispatcher.py:426
super().__init__(name=name)
self._orig_path = path
pattern = ""
formatter = ""
for part in ROUTE_RE.split(path):
match = self.DYN.fullmatch(part)
if match:
pattern += "(?P<{}>{})".format(match.group("var"), self.GOOD)
formatter += "{" + match.group("var") + "}"
continue
match = self.DYN_WITH_RE.fullmatch(part)
if match:
pattern += "(?P<{var}>{re})".format(**match.groupdict())
formatter += "{" + match.group("var") + "}"
continue
if "{" in part or "}" in part:
raise ValueError(f"Invalid path '{path}'['{part}']")
part = _requote_path(part)
formatter += part
pattern += re.escape(part)
try:
compiled = re.compile(pattern)
except re.error as exc:
raise ValueError(f"Bad pattern '{pattern}': {exc}") from None
assert compiled.pattern.startswith(PATH_SEP)
assert formatter.startswith("/")
self._pattern = compiled
self._formatter = formatter
@property
def canonical(self) -> str:
return self._formatter
View on GitHub (pinned to c0ef574e29)
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.
Example fix
# before
app.router.add_get('/users/{id', get_user)
# after
app.router.add_get('/users/{id}', get_user) Defensive patterns
Strategy: validation
Validate before calling
import re
_BRACE_OK = re.compile(r'^(?:[^{}]*|\{[_a-zA-Z][_a-zA-Z0-9]*(?::[^{}]+)?\})*$')
def validate_path(path: str) -> str:
if not _BRACE_OK.fullmatch(path):
raise ValueError(f"Invalid path template (unmatched braces): {path!r}")
return path Type guard
import re
_valid = re.compile(r'^(?:[^{}]*|\{[_a-zA-Z][_a-zA-Z0-9]*(?::[^{}]+)?\})*$')
def is_valid_path(path: str) -> bool:
return isinstance(path, str) and bool(_valid.fullmatch(path)) Try / catch
try:
app.router.add_get(path, handler)
except ValueError as e:
raise ValueError(f"Bad route path {path!r}: {e}") from e Prevention
- 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 '}'.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- {method} is not allowed HTTP method
- Bad pattern '{pattern}': {exc}
- Added route will never be executed, method {route.method} is
- Only async functions are allowed as web-handlers, got {handl
- Cannot change apps stack after .freeze() call
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/71edf431c0cdd815.json.
Report an issue: GitHub.