@@ -58,6 +58,9 @@ async def crash(request):
5858 async def return_error (request ):
5959 return PlainTextResponse ("Something went wrong" , status_code = 503 )
6060
61+ async def return_unauthorized (request ):
62+ return PlainTextResponse ("Unauthorized" , status_code = 401 )
63+
6164 async def background_jobs (request ):
6265 def sync_noop ():
6366 pass
@@ -85,6 +88,7 @@ async def __call__(self, scope, receive, send):
8588 Route ("/sync-hello/" , endpoint = SyncHelloEndpoint ),
8689 Route ("/crash/" , endpoint = crash ),
8790 Route ("/return-error/" , endpoint = return_error ),
91+ Route ("/return-unauthorized/" , endpoint = return_unauthorized ),
8892 Route ("/background-jobs/" , endpoint = background_jobs ),
8993 Route ("/instance-app/" , endpoint = InstanceApp ()),
9094 ]
@@ -474,3 +478,31 @@ async def test_instance_app(tracked_requests):
474478 "Controller/tests.integration.test_starlette."
475479 + "app_with_scout.<locals>.InstanceApp"
476480 )
481+
482+
483+ @pytest .mark .asyncio
484+ async def test_return_unauthorized_not_tagged_as_error (tracked_requests ):
485+ """
486+ Verify that a 401 Unauthorized response is tracked but NOT tagged as an
487+ error. Only 5xx responses should be tagged as errors. This is the correct
488+ behavior when, for example, a FastAPI OAuth2 dependency rejects an empty
489+ bearer token: the 401 is a normal client error, not a server error.
490+
491+ See: https://github.com/scoutapp/scout_apm_python/issues/838
492+ """
493+ with app_with_scout () as app :
494+ communicator = ApplicationCommunicator (
495+ app , asgi_http_scope (path = "/return-unauthorized/" )
496+ )
497+ await communicator .send_input ({"type" : "http.request" })
498+ response_start = await communicator .receive_output ()
499+ await communicator .receive_output ()
500+
501+ assert response_start ["type" ] == "http.response.start"
502+ assert response_start ["status" ] == 401
503+ assert len (tracked_requests ) == 1
504+ tracked_request = tracked_requests [0 ]
505+ assert len (tracked_request .complete_spans ) == 1
506+ assert tracked_request .tags ["path" ] == "/return-unauthorized/"
507+ # 401 must NOT be tagged as an error — only 5xx responses are errors
508+ assert "error" not in tracked_request .tags
0 commit comments