Skip to content

[Bug] exceptions's traceback are silently ignored with router in handle_request_async #17

Description

@flapili

Environment

Python 3.14.4 on Macos,
1.0.0rc1 + starlette 0.46.2 (as extra)

Bug Description

Hi, I'm trying FastOpenAPI for a poc and to return in my first love language 😅

when an unhandled exception in raised in FastOpenAPI's routers the behavior of errors handling differ from default/builtin, may lead to difficulties to identifies root cause of 500 without wrapping the whole handler code in try + log + re raise

pure starlette => print the stacktrace to stderr
with FastOpenApi => only access log show the 500 status code

I digged a bit with starlette but I believe all others frameworks are affected, it's not related to the asgi server as uvicorn and granian show the same issue, aiohttp too (however I did not check if aiohttp print traceback by default)

was able to isolate the cause to BaseAdapter.handle_request_async that eat the exception to build http error

then i'm wondering if it's not possible to have generic error handling regardless of the framework, it could just be a callable attached to the router

I'm open to contribute and make a PR I just want to ensure this feature would be suitable and not too out of scope of original lib goal

Steps to Reproduce

import logging
from fastopenapi import Response
import inspect
from fastopenapi.routers import StarletteRouter
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route


async def root(_request):
    1 / 0
    return JSONResponse(content={"hello": "world"})


app = Starlette(
    debug=True,
    routes=[
        Route("/1", root),
    ],
)

router = StarletteRouter(app=app)


@router.get("/2")
async def root_fastopenapi():
    1 / 0
    return JSONResponse({"hello": "world"})


async def handle_request_async(endpoint, env):
    """Handle asynchronous request"""
    try:
        request_data = await router.extractor_async_cls.extract_request_data(env)
        kwargs = await router.req_param_resolver_cls.resolve_async(
            endpoint, request_data
        )
        if inspect.iscoroutinefunction(endpoint):
            result = await endpoint(**kwargs)
        else:
            result = endpoint(**kwargs)
        route_meta = endpoint.__route_meta__
        response_model = route_meta.get("response_model")
        if response_model:
            result = router._validate_response(result, response_model)
        if router.is_framework_response(result):
            return result
        response = router.response_builder_cls.build(result, route_meta)
        if route_meta.get("status_code") == 204:
            response.content = None
        return router.build_framework_response(response)
    except Exception as e:
        logging.exception(e, exc_info=True) # <- e is used to build api response but not used at all for debugging purpose
        return router.build_framework_response(
            Response(
                content="ugly monkeypatch",
                status_code=500,
            )
        )

router.handle_request_async = handle_request_async

GET /1 => logged error and return 500 to client
GET /2 => return 500 to client but silent underlying error

Logs or Tracebacks

N/A 

Contact

@ mention me on github

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions