From 911cc7a30eeb979e7158f91d323631f9a6ecdd48 Mon Sep 17 00:00:00 2001 From: rasadov Date: Tue, 20 Jan 2026 15:43:57 +0400 Subject: [PATCH 1/2] fix: handle exceptions without 'detail' attribute in error handler - Change _rate_limit_exceeded_handler to accept Exception instead of RateLimitExceeded - Use getattr() to safely access exc.detail with fallback - Prevents AttributeError when ConnectionError or other exceptions occur - Add tests for ConnectionError and RateLimitExceeded scenarios Fixes #253 --- slowapi/extension.py | 13 +++++++-- tests/test_fastapi_extension.py | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/slowapi/extension.py b/slowapi/extension.py index 050f882..15c5071 100644 --- a/slowapi/extension.py +++ b/slowapi/extension.py @@ -73,13 +73,22 @@ class HEADERS: MAX_BACKEND_CHECKS = 5 -def _rate_limit_exceeded_handler(request: Request, exc: RateLimitExceeded) -> Response: +def _rate_limit_exceeded_handler(request: Request, exc: Exception) -> Response: """ Build a simple JSON response that includes the details of the rate limit that was hit. If no limit is hit, the countdown is added to headers. + + Handles exceptions that may not have a 'detail' attribute (e.g., ConnectionError). """ + # Safely get detail attribute, fallback to exception message if not available + if isinstance(exc, RateLimitExceeded): + detail = getattr(exc, 'detail', "Rate limit exceeded") + else: + # For other exceptions (e.g., ConnectionError), use exception message + detail = str(exc) + response = JSONResponse( - {"error": f"Rate limit exceeded: {exc.detail}"}, status_code=429 + {"error": f"Rate limit exceeded: {detail}"}, status_code=429 ) response = request.app.state.limiter._inject_headers( response, request.state.view_rate_limit diff --git a/tests/test_fastapi_extension.py b/tests/test_fastapi_extension.py index 42e6322..8340909 100644 --- a/tests/test_fastapi_extension.py +++ b/tests/test_fastapi_extension.py @@ -5,6 +5,7 @@ from starlette.testclient import TestClient from slowapi.util import get_ipaddr +from slowapi.extension import _rate_limit_exceeded_handler from tests import TestSlowapi @@ -369,3 +370,54 @@ async def t1_func(my_param: str, request: Request): ) == 2 ) + + def test_rate_limit_exceeded_handler_with_detail(self, build_fastapi_app): + """Test that RateLimitExceeded exceptions with detail attribute work correctly.""" + + app, limiter = build_fastapi_app(key_func=get_ipaddr) + + @app.get("/test") + @limiter.limit("1/minute") + async def test_endpoint(request: Request): + return {"message": "test"} + + client = TestClient(app) + + # Make first request - should succeed + response1 = client.get("/test") + assert response1.status_code == 200 + + # Make second request - should be rate limited + response2 = client.get("/test") + assert response2.status_code == 429 + assert "error" in response2.json() + assert "Rate limit exceeded" in response2.json()["error"] + + def test_rate_limit_exceeded_handler_without_detail(self, build_fastapi_app): + """Test that exceptions without 'detail' attribute are handled gracefully. + + This tests the fix for issue #213 where ConnectionError or other exceptions + without a 'detail' attribute would cause AttributeError. + """ + app, limiter = build_fastapi_app(key_func=get_ipaddr) + client = TestClient(app) + + # Create an exception without a 'detail' attribute (simulating ConnectionError) + class ExceptionWithoutDetail(Exception): + def __init__(self): + super().__init__("Connection failed") + + @app.get("/test") + async def test_endpoint(request: Request): + # Manually trigger the handler with an exception without detail + # This simulates the bug scenario from issue #213 + exc = ExceptionWithoutDetail() + response = _rate_limit_exceeded_handler(request, exc) + return response + + # Should not crash with AttributeError + response = client.get("/test") + assert response.status_code == 429 + assert "error" in response.json() + assert "Rate limit exceeded" in response.json()["error"] + assert "Connection failed" in response.json()["error"] From 78bb48cdfba59e3d9f1c764a18db807bfa50168c Mon Sep 17 00:00:00 2001 From: rasadov Date: Tue, 20 Jan 2026 15:47:18 +0400 Subject: [PATCH 2/2] test: add tests for exception handler without detail attribute Adds comprehensive test coverage for the exception handler fix, ensuring both RateLimitExceeded and exceptions without detail attributes are handled correctly.