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
- Ensure every segment is a valid identifier: starts with a letter or underscore, contains only letters/digits/underscores.
- Replace illegal characters: name = re.sub(r'[^0-9a-zA-Z_]+', '_', raw).strip('_').
- 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
- Restrict route names to [a-zA-Z_][a-zA-Z0-9_]* per segment, joined by . / : / -.
- Avoid leading digits and punctuation in name segments.
- Sanitise names generated from external data before registering.
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
- Incorrect route name
- Domain not valid
- Duplicate , already handled by
- path should be started with / or be empty
- Scheme not supported
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)