Exceptions
HTTP and WebSocket exception classes.
Exception classes for genro-asgi HTTP and WebSocket error handling.
This module provides typed exceptions for signaling errors in the HTTP request/response cycle and WebSocket connections. These exceptions are designed to be caught by the framework and converted to appropriate HTTP responses or WebSocket close frames.
Module Structure
Three exception classes, all inheriting directly from Exception:
HTTPException - For HTTP error responses (4xx, 5xx)
WebSocketException - For closing WebSocket with error code
WebSocketDisconnect - Signal that client disconnected (not an error)
Design Decisions
No validation: status_code and code are not validated. Users are expected to use appropriate values (4xx/5xx for HTTP, 1000-4999 for WebSocket).
No __slots__: Exceptions are short-lived and don’t benefit significantly from __slots__. This also maintains compatibility with Exception base class.
No common base: Each exception inherits directly from Exception for simplicity. Use tuple syntax for catching multiple: except (HTTPException, WebSocketException)
headers: Accepts both dict[str, str] for simple headers and list[tuple[str, str]] for headers that may have duplicate names (e.g., multiple Set-Cookie headers).
HTTPException
Raise in handlers to return an HTTP error response.
- genro_asgi.exceptions.headers
Optional response headers as list of tuples. Supports duplicate header names (e.g., multiple Set-Cookie). Input can be dict[str, str] or list[tuple[str, str]], stored as list.
Example
>>> raise HTTPException(404, detail="User not found")
>>> raise HTTPException(401, detail="Auth required", headers={"WWW-Authenticate": "Bearer"})
>>> raise HTTPException(400, headers=[("Set-Cookie", "a=1"), ("Set-Cookie", "b=2")])
WebSocketException
Raise to close a WebSocket connection with an error code.
- WebSocket close codes (RFC 6455):
1000 - Normal closure 1001 - Going away 1002 - Protocol error 1003 - Unsupported data 1007 - Invalid payload 1008 - Policy violation 1009 - Message too big 1011 - Internal error 4000-4999 - Application-specific (free to use)
Example
>>> raise WebSocketException(code=4000, reason="Invalid message format")
>>> raise WebSocketException(code=1008, reason="Rate limit exceeded")
WebSocketDisconnect
Raised by the framework when a WebSocket client disconnects. This is NOT an error - it’s a signal for cleanup.
Example
>>> try:
... data = await websocket.receive_text()
... except WebSocketDisconnect:
... logger.info("Client disconnected normally")
Difference between WebSocketException and WebSocketDisconnect:
- WebSocketException:
Raised BY the server code (explicit raise)
Semantics: “I want to close this connection with an error”
Typical handling: Log error, cleanup
- WebSocketDisconnect:
Raised BY the framework (when receive fails)
Semantics: “The client has disconnected”
Typical handling: Normal cleanup, no error logging
- Usage Pattern:
>>> async def websocket_handler(websocket): ... try: ... while True: ... msg = await websocket.receive_json() ... if not validate(msg): ... raise WebSocketException(4000, "Invalid format") ... await process(msg) ... except WebSocketDisconnect: ... logger.info("Client left") ... except WebSocketException as e: ... logger.error(f"WebSocket error: {e.code} - {e.reason}")
- exception genro_asgi.exceptions.HTTPBadRequest(detail='Bad request')[source]
Bases:
HTTPExceptionHTTP 400 Bad Request exception.
- exception genro_asgi.exceptions.HTTPException(status_code, detail='', headers=None)[source]
Bases:
ExceptionHTTP exception with status code and detail.
Raise this in handlers to return an HTTP error response. The framework will catch this and convert it to an appropriate HTTP response with the given status code, detail, and headers.
- status_code
HTTP status code (expected 4xx or 5xx, not validated)
- detail
Error detail message
- headers
Response headers as list of tuples (supports duplicate names)
Example
>>> raise HTTPException(404, detail="User not found") >>> raise HTTPException(401, headers={"WWW-Authenticate": "Bearer"}) >>> raise HTTPException(400, headers=[("Set-Cookie", "a=1"), ("Set-Cookie", "b=2")])
- exception genro_asgi.exceptions.HTTPForbidden(detail='Forbidden')[source]
Bases:
HTTPExceptionHTTP 403 Forbidden exception.
- exception genro_asgi.exceptions.HTTPNotFound(detail='Not found')[source]
Bases:
HTTPExceptionHTTP 404 Not Found exception.
Bases:
HTTPExceptionHTTP 503 Service Unavailable exception.
- exception genro_asgi.exceptions.HTTPUnauthorized(detail='Unauthorized')[source]
Bases:
HTTPExceptionHTTP 401 Unauthorized exception.
- exception genro_asgi.exceptions.Redirect(url, status_code=302)[source]
Bases:
HTTPExceptionHTTP redirect exception. Raises 302 redirect by default.
- exception genro_asgi.exceptions.WebSocketDisconnect(code=1000, reason='')[source]
Bases:
ExceptionRaised when a WebSocket is disconnected by the client.
This is not an error, just a signal that the connection was closed. The framework raises this when a receive operation fails because the client has disconnected.
- code
WebSocket close code from client
- reason
Close reason from client (if any)
Example
>>> try: ... data = await websocket.receive_text() ... except WebSocketDisconnect as e: ... print(f"Client disconnected: {e.code}")
- exception genro_asgi.exceptions.WebSocketException(code=1000, reason='')[source]
Bases:
ExceptionWebSocket exception with close code and reason.
Raise this to close a WebSocket connection with an error code. The framework will catch this and send a close frame with the given code and reason.
- code
WebSocket close code (1000-4999, not validated)
- reason
Close reason message
Example
>>> raise WebSocketException(code=4000, reason="Invalid message")