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

  1. Balance all braces: '/users/{id}' with matching opening and closing.
  2. For a variable with a custom regex use '/items/{id:[0-9]+}'.
  3. Avoid literal braces in URLs; if unavoidable, URL-encode them and use a static prefix.
  4. 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

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


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/71edf431c0cdd815.json. Report an issue: GitHub.