Browse documentation

Documentation overview

Tutorial

Reference

Project

Errors & troubleshooting

An exception and an HTTP error response are different things. Your logs would appreciate being invited to this distinction.

Exceptions Wred can raise

ExceptionTypical triggerWhat to do
orjson.JSONDecodeErrorInvalid JSON in await request.json().Catch at the input boundary and return a 400 response.
UnicodeDecodeErrorInvalid UTF-8 in the raw or percent-decoded query string.Reject the query with 400 if it is part of your public input handling.
ConnectionErrorA body read receives a disconnect or another non-request event.Handle cancellation/disconnection where relevant; do not assume a response can still reach the client.
TypeErrorAn unsupported response body, or a serialization/type error.Return bytes, a dict/list, a supported tuple, or a callable ASGI response.
ValueErrorFor example, unpacking a tuple with fewer than three response fields.Use exactly the documented three- or four-item response tuple.
UnicodeEncodeErrorA custom header name is not ASCII or a value cannot be encoded as Latin-1.Use HTTP-compatible headers and encode internationalized values with the appropriate header format.
RuntimeErrorA non-HTTP scope reaches a bare Wred app.Route that protocol to an outer app or configure lifespan appropriately.

Wred lets these exceptions propagate. Your middleware or ASGI server decides how to log and respond to an unhandled exception. Do not depend on a bare Wred app to generate a JSON 500 response.

Return expected errors explicitly

Add to an existing app
import orjson


@app.route('/api/items', methods=['POST'])
async def create_item(request):
    try:
        data = await request.json()
    except orjson.JSONDecodeError:
        return {'error': 'Invalid JSON'}, 'application/json', 400
    if not isinstance(data, dict):
        return {'error': 'Send a JSON object'}, 'application/json', 400
    return {'accepted': True}

Catch the exception you expect at the boundary you understand. A blanket except Exception: return {"ok": True} makes dashboards look lovely and everything else substantially worse.

Where HTTP statuses come from

StatusWred behavior
200Default for supported handler return values.
400 / 401 / 403 / 409 / 429Application or middleware decisions. The notebook provides examples.
404Wred’s fallback for an unmatched path or method.
405Not generated automatically. Register a handler or use another routing layer if you need it.
500Unhandled-error behavior is supplied by the server or middleware. See the error middleware example.

HTTP status is part of the response, not a declaration made by throwing a Starlette exception. Wred does not install Starlette’s exception handlers for you.

A short debugging sequence

  1. Call the backend directly from its host and inspect the exact status, headers, and body with curl -i.
  2. Confirm the server imports the wrapper you intended, not just the inner Wred instance.
  3. Check the route path and method, then the proxy or mount prefix.
  4. Inspect the server traceback. Turn off public debug tracebacks before deployment.
  5. Repeat through the public proxy to isolate scheme, Host, cookie, and forwarding issues.

The operations troubleshooting table covers 502s, database startup failures, login problems, and buffered streams.

After a response has started

A callable response owns its send operations. If it raises after sending headers or part of a stream, Wred propagates the exception without attempting a second response. Validate inputs and permissions before sending the first message.

An error handler cannot change a status code already sent to the client. Time travel remains an optional dependency that nobody has successfully published.