{"record":{"id":"e1ba41448081eb33","repo":"aio-libs/aiohttp","slug":"incorrect-route-name-name-r-the-name-should-be","errorCode":null,"errorMessage":"Incorrect route name {name!r}, the name should be a sequence of python identifiers separated by dash, dot or column","messagePattern":"Incorrect route name (.+?), the name should be a sequence of python identifiers separated by dash, dot or column","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_urldispatcher.py","lineNumber":1059,"sourceCode":"        assert isinstance(\n            resource, AbstractResource\n        ), f\"Instance of AbstractResource class is required, got {resource!r}\"\n        if self.frozen:\n            raise RuntimeError(\"Cannot register a resource into frozen router.\")\n\n        name = resource.name\n\n        if name is not None:\n            parts = self.NAME_SPLIT_RE.split(name)\n            for part in parts:\n                if keyword.iskeyword(part):\n                    raise ValueError(\n                        f\"Incorrect route name {name!r}, \"\n                        \"python keywords cannot be used \"\n                        \"for route name\"\n                    )\n                if not part.isidentifier():\n                    raise ValueError(\n                        f\"Incorrect route name {name!r}, \"\n                        \"the name should be a sequence of \"\n                        \"python identifiers separated \"\n                        \"by dash, dot or column\"\n                    )\n            if name in self._named_resources:\n                raise ValueError(\n                    f\"Duplicate {name!r}, \"\n                    f\"already handled by {self._named_resources[name]!r}\"\n                )\n            self._named_resources[name] = resource\n        self._resources.append(resource)\n\n        if isinstance(resource, MatchedSubAppResource):\n            # We cannot index match sub-app resources because they have match rules\n            self._matched_sub_app_resources.append(resource)\n        else:\n            self.index_resource(resource)","sourceCodeStart":1041,"sourceCodeEnd":1077,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/web_urldispatcher.py#L1041-L1077","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\napp.router.add_get('/v2-users', h, name='2-users')\n// after\napp.router.add_get('/v2-users', h, name='v2_users')","handlingStrategy":"validation","validationCode":"import re\n\ndef safe_route_name(name: str) -> str:\n    cleaned = re.sub(r'[^0-9a-zA-Z_.:-]+', '_', name).strip('_.:-')\n    for part in re.split(r'[.:-]', cleaned):\n        if not part.isidentifier():\n            raise ValueError(f'route name segment {part!r} is not a valid identifier')\n    return cleaned","typeGuard":"import re\n\ndef is_valid_route_name(name: str) -> bool:\n    return (\n        isinstance(name, str)\n        and bool(name)\n        and all(p.isidentifier() for p in re.split(r'[.:-]', name))\n    )","tryCatchPattern":"try:\n    app.router.add_get('/x', h, name=name)\nexcept ValueError as e:\n    if 'identifier' in str(e):\n        name = re.sub(r'\\W+', '_', name).strip('_')\n        if name and not name[0].isalpha():\n            name = '_' + name\n        app.router.add_get('/x', h, name=name)\n    else:\n        raise","preventionTips":["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."],"tags":["url-dispatcher","route-naming","validation","identifiers"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}