{"record":{"id":"bcdce748af7100be","repo":"aio-libs/aiohttp","slug":"http-redirects-need-a-location-to-redirect-to","errorCode":null,"errorMessage":"HTTP redirects need a location to redirect to.","messagePattern":"HTTP redirects need a location to redirect to\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_exceptions.py","lineNumber":230,"sourceCode":"\n\n############################################################\n# 3xx redirection\n############################################################\n\n\nclass HTTPMove(HTTPRedirection):\n    def __init__(\n        self,\n        location: StrOrURL,\n        *,\n        headers: LooseHeaders | None = None,\n        reason: str | None = None,\n        text: str | None = None,\n        content_type: str | None = None,\n    ) -> None:\n        if not location:\n            raise ValueError(\"HTTP redirects need a location to redirect to.\")\n        super().__init__(\n            headers=headers, reason=reason, text=text, content_type=content_type\n        )\n        self._location = URL(location)\n        self.headers[\"Location\"] = str(self.location)\n\n    @property\n    def location(self) -> URL:\n        return self._location\n\n\nclass HTTPMultipleChoices(HTTPMove):\n    status_code = 300\n\n\nclass HTTPMovedPermanently(HTTPMove):\n    status_code = 301\n","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/web_exceptions.py#L212-L248","documentation":"HTTPMove subclasses (HTTPMovedPermanently, HTTPFound, HTTPSeeOther, HTTPTemporaryRedirect, HTTPPermanentRedirect) require a truthy location. A 3xx redirect without a Location header is invalid HTTP, so the constructor rejects falsy locations (empty string, None, empty URL).","triggerScenarios":"HTTPFound(''), HTTPMovedPermanently(None), or building the location from a request header that is absent so it defaults to empty.","commonSituations":"Redirecting to a URL computed from Referer/next params that the client omitted; conditional redirect where the target ends up empty; passing a yarl.URL built from an empty string.","solutions":["Validate the location is non-empty before constructing the redirect.","Provide a safe fallback URL when the source is empty.","Build the URL with yarl.URL and confirm it is absolute before redirecting."],"exampleFix":"# before\ntarget = request.query.get('next', '')\nraise HTTPFound(target)\n# after\ntarget = request.query.get('next') or '/'\nif not target.startswith('/'):\n    target = '/'\nraise HTTPFound(target)","handlingStrategy":"validation","validationCode":"if not location:\n    location = '/'\nraise web.HTTPFound(location)","typeGuard":"def has_location(loc: object) -> TypeGuard[str | URL]:\n    return bool(loc)","tryCatchPattern":null,"preventionTips":["Default redirect targets to a safe path like '/'.","Whitelist redirect destinations to prevent open-redirect.","Build the URL with yarl.URL and assert it is absolute."],"tags":["redirect","validation","http-exception"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}