aio-libs/aiohttp · error · ValueError

Invalid path ' '[' ']

Error message

Invalid path '{path}'['{part}']

What it means

While parsing a path template (e.g. '/users/{id}'), the PatternBuilder splits on '/' and processes each segment. If a segment contains a '{' or '}' that does not match the dynamic-variable syntax (DYN or DYN_WITH_RE forms like '{name}' or '{name:regex}'), the parser cannot interpret it and raises ValueError. This catches malformed path templates before they compile into a regex that would match unexpected things.

Solutions

  1. Use braces only to declare path variables: '/users/{id}' or '/users/{id:\d+}'.
  2. Remove any literal '{' or '}' that is not part of a variable declaration.
  3. If you need a literal brace in the URL, avoid the template engine (register a catch-all and parse manually) or restructure the path.

Example fix

// before
app.router.add_get('/users/{id', handler)   # unclosed brace
// after
app.router.add_get('/users/{id}', handler)  # closed
Defensive patterns

Strategy: validation

Validate before calling

import re

def validate_path_template(path: str) -> None:
    for segment in path.strip('/').split('/'):
        # allow {var} or {var:re}; flag stray braces
        stripped = re.sub(r'\{[^{}]*(:[^{}]*)?\}', '', segment)
        if '{' in stripped or '}' in stripped:
            raise ValueError(f'Invalid path segment: {segment!r}')

Prevention

When it happens

Trigger: Calling app.router.add_route with a path containing unmatched or misplaced braces: '/users/{', '/users/}', '/users/{id{', '/users/}id', '/users/foo}bar', or a segment like '/a{b' where a brace is not part of a valid {var} or {var:re} group.

Common situations: Typos in path templates; attempting to use literal braces in a path without escaping (braces are reserved for variables); partial copy-paste of a dynamic route; path templates built by string concatenation that accidentally leaves stray braces.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/71edf431c0cdd815. Report an issue: GitHub.

Appendix: 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 d041d4d0fd)