What's happening
Every read endpoint wraps its streaming logic in a bare except Exception block and hardcodes 404 Not Found as the response , no matter what actually went wrong.
try:
first_item = await anext(result)
return StreamingResponse(...)
except Exception as e:
return JSONResponse(
status_code=status.HTTP_404_NOT_FOUND,
content={"code": 404, "type": "error", "message": "Not Found"},
)
So if the database goes down, the client gets 404. If there's an unhandled bug in the service layer, the client gets 404. The only case where 404 is actually correct ,no rows returned ,also gets 404. Everything looks the same from the outside.
Why it matters
Clients can't distinguish "no data" from "service is broken" .they'll retry or report a missing resource when the real problem is a backend outage.
Ops/monitoring is blind ,alerts keyed on 5xx errors will never fire for database failures coming through these endpoints.
Debugging is painful ,logs swallow the real exception; you just see 404s.
Root cause
StopAsyncIteration is the only exception that should map to 404 — it means the async generator returned nothing. Every other exception is either a database problem (→ 503) or an unexpected bug (→ 500).
The correct pattern already exists in the codebase:
except StopAsyncIteration:
return JSONResponse(status_code=404, ...)
except (PostgresConnectionError, TooManyConnectionsError):
return JSONResponse(status_code=503, ...)
except Exception as e:
logger.exception(e)
return JSONResponse(status_code=500, ...)
The individual read endpoints just never adopted it.
What's happening
Every read endpoint wraps its streaming logic in a bare except Exception block and hardcodes 404 Not Found as the response , no matter what actually went wrong.
So if the database goes down, the client gets 404. If there's an unhandled bug in the service layer, the client gets 404. The only case where 404 is actually correct ,no rows returned ,also gets 404. Everything looks the same from the outside.
Why it matters
Clients can't distinguish "no data" from "service is broken" .they'll retry or report a missing resource when the real problem is a backend outage.
Ops/monitoring is blind ,alerts keyed on 5xx errors will never fire for database failures coming through these endpoints.
Debugging is painful ,logs swallow the real exception; you just see 404s.
Root cause
StopAsyncIteration is the only exception that should map to 404 — it means the async generator returned nothing. Every other exception is either a database problem (→ 503) or an unexpected bug (→ 500).
The correct pattern already exists in the codebase:
The individual read endpoints just never adopted it.