Exception

ahttp_client provides a ready-made exception class for every standard HTTP 4xx/5xx status code, so error handling does not require checking response.status manually and raising a custom exception.

Raising an HTTPException from a Response
 1import aiohttp
 2
 3from ahttp_client import AsyncSession, HTTPNotFound, Response
 4
 5
 6class GithubService(AsyncSession):
 7    async def after_request(self, response: Response) -> Response:
 8        response.raise_for_status()
 9        return response
10
11async with GithubService("https://api.github.com", aiohttp.ClientSession) as service:
12    try:
13        await service.list_repositories(user="does-not-exist")
14    except HTTPNotFound as exc:
15        print(exc.status, exc.url)

Response.raise_for_status() looks up the exception class registered for the response’s status code and raises it, response attached, if it finds one. A successful or redirect response (2xx/3xx) passes through untouched.

Request Status Validation

Set raise_on=True on @request or an HTTP method decorator when an endpoint accepts only HTTP 200. The request execution path validates the raw response before it is returned or deserialized. Every other status raises an exception carrying that response: 4xx and 5xx responses use their registered subclass (or HTTPClientError/HTTPServerError), while other statuses use the base HTTPException. The response is closed before the exception is raised.

class GithubService(AsyncSession):
    @get("/health", raise_on=True)
    async def health(self) -> None:
        ...

Unlike Response.raise_for_status(), raise_on=True rejects all statuses other than 200, including successful alternatives such as 201 or 204 and redirects.

Exception Hierarchy

Every exception inherits from HTTPException, which carries the response that triggered it along with its status code and url.

  • HTTPClientError - the base class for 4xx responses, used directly whenever a 4xx status has no dedicated class of its own.

  • HTTPServerError - the base class for 5xx responses, used the same way.

  • Beyond those two, there is one concrete subclass per standard status code: HTTPBadRequest (400), HTTPNotFound (404), HTTPTooManyRequests (429), HTTPInternalServerError (500), HTTPServiceUnavailable (503), and so on.

Note

RFC 9110 renamed a few statuses; both the current and legacy names are exported as aliases of the same class: HTTPPayloadTooLarge/HTTPContentTooLarge (413), HTTPRequestURITooLong/HTTPURITooLong (414), and HTTPUnprocessableEntity/HTTPUnprocessableContent (422).

Catching by category instead of exact status code comes in handy when every 4xx (or every 5xx) response should be retried or logged the same way:

from ahttp_client import HTTPClientError, HTTPServerError

try:
    await service.list_repositories(user="gunyu1019")
except HTTPClientError:
    ...  # any 4xx status
except HTTPServerError:
    ...  # any 5xx status

Combining with Retry

Because raise_for_status() turns a failing response into a typed exception, that exception can be listed directly in retry_on (see Retry) to retry only on server errors, without a manual status check:

from ahttp_client import HTTPServerError, retry

class GithubService(AsyncSession):
    async def after_request(self, response: Response) -> Response:
        response.raise_for_status()
        return response

    @retry(retry_on=(HTTPServerError,))
    @get("/users/{user}/repos")
    async def list_repositories(self, response: Response, user: Annotated[str, Path]):
        return response.json()

raise_on=True performs the same typed conversion for 4xx and 5xx statuses while also rejecting every non-200 status, so it can be used directly with the default retry_on=(HTTPServerError,) configuration:

@retry(max_retries=3)
@get("/health", raise_on=True)
async def health(self, response: Response):
    return response.json()

Use retry_on_status instead when the response should be retried without raising an exception; see Retry.

Reference

class HTTPException

Base exception for an unsuccessful HTTP response.

Each concrete status exception declares its HTTP status_code and human-readable title as class variables. The original response is retained when the exception is raised through raise_for_status().

class HTTPClientError

Bases: HTTPException

Base exception for an unrecognised 4xx client error response.

class HTTPServerError

Bases: HTTPException

Base exception for an unrecognised 5xx server error response.

exception_for_status(status_code: int) → type[HTTPException] | None

Return the exception class associated with an unsuccessful status code.

Registered HTTP status codes use their concrete exception class. Other values in the 4xx and 5xx ranges use their respective category base class. Successful and redirect responses return None.