aio-libs/aiohttp · error · ValueError

Incorrect route name

Error message

Incorrect route name {name!r}, the name should be a sequence of python identifiers separated by dash, dot or column

What it means

Companion to error 264: each dot/dash/colon-separated segment of the route name must be a valid Python identifier (str.isidentifier()). Segments starting with a digit, containing spaces/special chars, or being empty (e.g. from '..' or a trailing '.') fail this check. The check runs after the keyword check and uses Python's own isidentifier() rule.

Solutions

  1. Ensure every segment is a valid identifier: starts with a letter or underscore, contains only letters/digits/underscores.
  2. Replace illegal characters: name = re.sub(r'[^0-9a-zA-Z_]+', '_', raw).strip('_').
  3. Avoid leading digits; prefix with '_' or a letter if needed.

Example fix

// before
app.router.add_get('/v2-users', h, name='2-users')
// after
app.router.add_get('/v2-users', h, name='v2_users')
Defensive patterns

Strategy: validation

Validate before calling

import re

def safe_route_name(name: str) -> str:
    cleaned = re.sub(r'[^0-9a-zA-Z_.:-]+', '_', name).strip('_.:-')
    for part in re.split(r'[.:-]', cleaned):
        if not part.isidentifier():
            raise ValueError(f'route name segment {part!r} is not a valid identifier')
    return cleaned

Type guard

import re

def is_valid_route_name(name: str) -> bool:
    return (
        isinstance(name, str)
        and bool(name)
        and all(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 'identifier' in str(e):
        name = re.sub(r'\W+', '_', name).strip('_')
        if name and not name[0].isalpha():
            name = '_' + name
        app.router.add_get('/x', h, name=name)
    else:
        raise

Prevention

When it happens

Trigger: app.router.add_get('/users', h, name='2users') (leading digit); name='user list' (space); name='user.' or '.user' (empty segment after split); name='user@list' ('@' not identifier-legal).

Common situations: Building names from URL paths verbatim (paths contain '/', which isn't split on); names derived from external data with punctuation; trailing separators from string concatenation.

Related errors


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

Appendix: source

Thrown at aiohttp/web_urldispatcher.py:1059

        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)

        if isinstance(resource, MatchedSubAppResource):
            # We cannot index match sub-app resources because they have match rules
            self._matched_sub_app_resources.append(resource)
        else:
            self.index_resource(resource)

View on GitHub (pinned to d041d4d0fd)