Skip to content

hosting: http_exception_handler drops HTTPException.headers, so a route cannot set Vary, Cache-Control or WWW-Authenticate on an error response #369

Description

@antosubash

Summary

The host's HTTPException handler rebuilds the response from status_code and detail only. Any headers a route attached to the exception are discarded. Starlette and FastAPI both honour HTTPException(headers=...) by default; the host's override silently loses them.

Observed

simple_module_hosting/_error_handlers.py:48-52 (0.0.26):

async def http_exception_handler(request: Request, exc: HTTPException) -> Response:
    if exc.status_code in _INERTIA_ERROR_STATUSES:
        detail = str(exc.detail) if exc.detail else ""
        return await render_error_page(request, exc.status_code, detail)
    return JSONResponse(status_code=exc.status_code, content={"detail": exc.detail})

Reproduction with any route:

@router.get("/x")
async def x():
    raise HTTPException(404, "nope", headers={"Cache-Control": "no-store", "Vary": "X-Tenant-ID"})
curl -i http://localhost:8000/api/<module>/x
HTTP/1.1 404 Not Found
content-type: application/json
...            # neither Cache-Control nor Vary is present

Why it matters

A public, cacheable read API on a multi-tenant host has to mark its 404s Cache-Control: no-store and Vary: <tenant header>, otherwise a shared cache keyed on the URL serves one tenant's 404 for another tenant's existing record. The same mechanism is how 401 carries WWW-Authenticate and 429 carries Retry-After. None of those can be set through the exception today.

Workaround in the records module

antosubash/smpy_modules#37 installs a custom APIRoute class (PublicErrorRoute) on its public router that catches HTTPException before the host handler and renders the JSON body itself so the headers survive. Every module wanting a header on an error response has to repeat that.

Proposed fix

    return JSONResponse(status_code=exc.status_code, content={"detail": exc.detail}, headers=exc.headers)

and pass exc.headers through render_error_page for the Inertia branch as well.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions