aio-libs/aiohttp · error · ValueError

Bad pattern '{pattern}': {exc}

Error message

Bad pattern '{pattern}': {exc}

What it means

Raised as ValueError by DynamicResource.__init__ when the compiled regex built from the path template fails to compile (re.error). After converting the template into a regex pattern, aiohttp runs re.compile; if the user-supplied regex inside {var:regex} is invalid, the underlying re.error is wrapped and re-raised with this message.

Source

Thrown at aiohttp/web_urldispatcher.py:435

                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

    def add_prefix(self, prefix: str) -> None:
        assert prefix.startswith("/")
        assert not prefix.endswith("/")
        assert len(prefix) > 1
        self._pattern = re.compile(re.escape(prefix) + self._pattern.pattern)
        self._formatter = prefix + self._formatter

    def _match(self, path: str) -> dict[str, str] | None:
        match = self._pattern.fullmatch(path)

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Test the regex in a Python shell with re.compile first, then paste it into the template.
  2. Use valid regex: '/items/{id:[0-9]+}' for digits, '/items/{slug:[a-z-]+}' for slugs.
  3. Remember the constraint is a full Python regex, not a glob — '*' alone is invalid.
  4. Escape literals like '.' as '\.' inside the constraint.

Example fix

# before
app.router.add_get('/items/{id:[0-9}', handler)

# after
app.router.add_get('/items/{id:[0-9]+}', handler)
Defensive patterns

Strategy: validation

Validate before calling

import re

def validate_regex_constraint(pattern: str) -> str:
    try:
        re.compile(pattern)
    except re.error as e:
        raise ValueError(f"Invalid regex in path constraint {pattern!r}: {e}") from None
    return pattern

Type guard

import re

def is_compilable_regex(pattern: str) -> bool:
    try:
        re.compile(pattern)
        return True
    except re.error:
        return False

Try / catch

try:
    app.router.add_get(path, handler)
except ValueError as e:
    raise ValueError(f"Route regex failed to compile: {e}") from e

Prevention

When it happens

Trigger: Using an invalid regex in a variable constraint: app.router.add_get('/items/{id:[0-9', handler) (unclosed char class), '{id:(foo}' (unbalanced group), '{id:*}' (* is not a valid regex standalone), or any PCRE syntax Python's re rejects.

Common situations: Hand-writing regex constraints without testing; copy-paste truncating the regex; assuming glob syntax ('*' matches all) instead of regex; nested unescaped special chars.

Related errors


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