aio-libs/aiohttp · error · ValueError

Incorrect route name

Error message

Incorrect route name {name!r}, python keywords cannot be used for route name

What it means

register_resource() splits the route name on '.', ':', '-' (NAME_SPLIT_RE) and rejects the resource if any segment is a Python keyword (class, def, return, async, etc.). Route names are used by app.router.named_resources and by url_for(), and aiohttp wants each segment to be a valid Python identifier so generated code or attribute-style lookups stay legal. This runs after the frozen check but before the duplicate check.

Solutions

  1. Rename the route so no dot/dash/colon-separated segment is a Python keyword (e.g. 'klass', 'item-import').
  2. When generating names from data, sanitize against keyword.iskeyword() before passing to add_route.
  3. Avoid names that match the full keyword list (keyword.kwlist).

Example fix

// before
app.router.add_get('/class/{id}', handler, name='class')
// after
app.router.add_get('/class/{id}', handler, name='klass')
Defensive patterns

Strategy: validation

Validate before calling

import keyword

def safe_route_name(name: str) -> str:
    for part in re.split(r'[.:-]', name):
        if keyword.iskeyword(part):
            raise ValueError(f'route name segment {part!r} is a python keyword')
    return name

name = safe_route_name(generated_name)
app.router.add_get('/x', h, name=name)

Type guard

import keyword, re

def is_valid_route_name(name: str) -> bool:
    return (
        isinstance(name, str)
        and all(
            not keyword.iskeyword(p) and p.isidentifier()
            for p in re.split(r'[.:-]', name)
        )
    )

Try / catch

try:
    app.router.add_get('/x', h, name=name)
except ValueError as e:
    if 'keyword' in str(e):
        name = name + '_route'  # disambiguate and retry
        app.router.add_get('/x', h, name=name)
    else:
        raise

Prevention

When it happens

Trigger: Calling app.router.add_get('class', ...) or add_route('GET', '/x', h, name='class'); using a multi-segment name like 'api.return' or 'auth.class' (each segment is checked); programmatically generating names from user input that happens to be a keyword.

Common situations: Auto-generating route names from resource nouns that collide with keywords ('class', 'import', 'global', 'lambda'); REST naming conventions that produce segments like 'import' or 'return'.

Related errors


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

Appendix: source

Thrown at aiohttp/web_urldispatcher.py:1053

        return RoutesView(self._resources)

    def named_resources(self) -> Mapping[str, AbstractResource]:
        return MappingProxyType(self._named_resources)

    def register_resource(self, resource: AbstractResource) -> None:
        assert isinstance(
            resource, AbstractResource
        ), f"Instance of AbstractResource class is required, got {resource!r}"
        if self.frozen:
            raise RuntimeError("Cannot register a resource into frozen router.")

        name = resource.name

        if name is not None:
            parts = self.NAME_SPLIT_RE.split(name)
            for part in parts:
                if keyword.iskeyword(part):
                    raise ValueError(
                        f"Incorrect route name {name!r}, "
                        "python keywords cannot be used "
                        "for route name"
                    )
                if not part.isidentifier():
                    raise ValueError(
                        f"Incorrect route name {name!r}, "
                        "the name should be a sequence of "
                        "python identifiers separated "
                        "by dash, dot or column"
                    )
            if name in self._named_resources:
                raise ValueError(
                    f"Duplicate {name!r}, "
                    f"already handled by {self._named_resources[name]!r}"
                )
            self._named_resources[name] = resource
        self._resources.append(resource)

View on GitHub (pinned to d041d4d0fd)