aio-libs/aiohttp · error · InvalidUrlRedirectClientError

Server attempted redirecting to a location that does not…

Error message

Server attempted redirecting to a location that does not look like a URL

What it means

Raised as InvalidUrlRedirectClientError when yarl.URL() raises ValueError parsing the server-supplied redirect target (LOCATION/URI header). The redirect target is structurally not a URL and cannot be followed.

Solutions

  1. Set allow_redirects=False and follow redirects manually so you can sanitize the LOCATION header.
  2. Fix or report the server returning the malformed redirect.
  3. If requote_redirect_url is the cause, set ClientSession(requote_redirect_url=False) (or True) to match the server's encoding convention.

Example fix

// before
await session.get(url)  # server returns malformed LOCATION -> raises
// after
resp = await session.get(url, allow_redirects=False)
if 300 <= resp.status < 400:
    next_url = URL(resp.headers['LOCATION'].strip())
    resp = await session.get(next_url)
Defensive patterns

Strategy: try-catch

Validate before calling

from aiohttp import URL
from aiohttp.client_exceptions import InvalidURL

def safe_redirect_target(raw):
    try:
        return URL(raw, encoded=False)
    except ValueError:
        return None

Type guard

from aiohttp import URL

def is_valid_redirect_target(raw) -> bool:
    try:
        URL(raw)
        return True
    except ValueError:
        return False

Try / catch

from aiohttp.client_exceptions import InvalidUrlRedirectClientError

try:
    resp = await session.get(url)
except InvalidUrlRedirectClientError as e:
    # disable auto-follow and decide manually
    resp = await session.get(url, allow_redirects=False)

Prevention

When it happens

Trigger: Server returns a 3xx with a LOCATION header containing characters/syntax yarl cannot parse (control chars, malformed quoting, illegal percent-encoding). self._requote_redirect_url changes the encoded= flag passed to URL().

Common situations: Misconfigured or hostile server returning garbage LOCATION headers. Buggy server-side URL builders. Encoded vs raw redirect URL mismatch when requote_redirect_url is disabled.

Related errors


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

Appendix: source

Thrown at aiohttp/client.py:824

                            hdrs.URI
                        )
                        if r_url is None:
                            # see github.com/aio-libs/aiohttp/issues/2022
                            break
                        else:
                            # reading from correct redirection
                            # response is forbidden
                            resp.release()

                        try:
                            parsed_redirect_url = URL(
                                r_url, encoded=not self._requote_redirect_url
                            )
                        except ValueError as e:
                            if req._body is not None:
                                await req._body.close()
                            resp.close()
                            raise InvalidUrlRedirectClientError(
                                r_url,
                                "Server attempted redirecting to a location that does not look like a URL",
                            ) from e

                        scheme = parsed_redirect_url.scheme
                        if scheme not in HTTP_AND_EMPTY_SCHEMA_SET:
                            if req._body is not None:
                                await req._body.close()
                            resp.close()
                            raise NonHttpUrlRedirectClientError(r_url)
                        elif not scheme:
                            parsed_redirect_url = url.join(parsed_redirect_url)

                        try:
                            redirect_origin = parsed_redirect_url.origin()
                        except ValueError as origin_val_err:
                            if req._body is not None:
                                await req._body.close()

View on GitHub (pinned to d041d4d0fd)