ModelEngine-Group / ModelEngine-Group/nexent

Two parallel exception-handling stacks emit incompatible error response shapes

Open
#3,375 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5.9k
Forks
731
Avg merge
19h 34m
Merged PRs (30d)
172

Description

The repo registers FastAPI error handlers in two different places, and they don't agree on the response schema.

Path Abackend/middleware/exception_handler.py:38 — installed as a BaseHTTPMiddleware:

return JSONResponse(
    status_code=http_status,
    content={
        "code": exc.error_code.value,
        "message": exc.message,
        "trace_id": trace_id,
        "details": exc.details if exc.details else None
    }
)

…and for plain HTTPException:

content={"code": error_code.value, "message": exc.detail, "trace_id": trace_id}

Path Bbackend/apps/app_factory.py:78-108 — registered via @app.exception_handler(...):

@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc):
    return JSONResponse(
        status_code=exc.status_code,
        content={"message": exc.detail},          # no code, no trace_id
    )

@app.exception_handler(Exception)
async def generic_exception_handler(request, exc):
    return JSONResponse(
        status_code=500,
        content={"message": "Internal server error, please try again later."},  # no code, no trace_id, generic message
    )
Why it matters
  • Frontend services/api.ts (and the various typed services that wrap fetchWithErrorHandling) can't rely on code or trace_id being present — they sometimes are, sometimes aren't, depending on which handler intercepted the exception. FastAPI's @app.exception_handler runs before BaseHTTPMiddleware for handled exceptions raised in route code, but middleware will still catch unhandled errors and wrap them with the richer schema — so the same logical error can return two different shapes depending on where it originated.
  • The middleware advertises a trace_id in every response. Operators using it as a correlation key will find that some 4xx and 5xx responses lack it entirely.
Suggested fix

Pick one. Either:

  • Remove register_exception_handlers from app_factory.py and rely on ExceptionHandlerMiddleware end-to-end, or
  • Update the app_factory.py handlers to emit the same {code, message, trace_id, details} shape, sourcing trace_id from request.state.trace_id (which the middleware sets).

Also worth checking: app_factory.py:48-54 sets allow_origins=["*"] together with allow_credentials=True, which is non-compliant per CORS spec and silently ignored by browsers. Probably should be a separate issue but mentioning here for context.

Severity: Medium. Visible to API consumers; harms debuggability.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read backend/middleware/exception_handler.py and backend/apps/app_factory.py first, comparing which exception paths produce each response shape and how request.state.trace_id is set. Verify the chosen approach makes HTTPException and generic errors consistently return code, message, trace_id, and details without changing the separate CORS concern.

Written by the indexing model from the issue text.

Assessment

Tech stack
fastapi, python
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
62/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.