diff --git a/apps/api/app/core/exception_handlers.py b/apps/api/app/core/exception_handlers.py index 7d5a72680..9e9e309c4 100644 --- a/apps/api/app/core/exception_handlers.py +++ b/apps/api/app/core/exception_handlers.py @@ -126,7 +126,7 @@ async def knowhere_exception_handler( # responses and ``WWW-Authenticate`` for authentication challenges. headers = dict(response_headers or {}) headers["X-Request-ID"] = request_id - retry_after = exc.details.get("retry_after") + retry_after = (exc.details or {}).get("retry_after") if retry_after: headers["Retry-After"] = str(retry_after) diff --git a/apps/api/tests/contract/test_exception_handlers_contract.py b/apps/api/tests/contract/test_exception_handlers_contract.py index 40eff1064..0fe189c14 100644 --- a/apps/api/tests/contract/test_exception_handlers_contract.py +++ b/apps/api/tests/contract/test_exception_handlers_contract.py @@ -38,6 +38,42 @@ async def query_retrieval() -> dict[str, bool]: return app +def _create_invalid_webhook_app() -> FastAPI: + _prepare_api_app_imports() + + from app.core.exception_handlers import setup_exception_handlers + from shared.core.exceptions.webhook_exceptions import WebhookConfigException + + app = FastAPI() + + @app.post("/v2/jobs") + async def create_job() -> dict[str, bool]: + raise WebhookConfigException( + user_message="Invalid webhook URL", + internal_message=( + "Webhook validation failed: Hostname localhost failed validation" + ), + ) + + setup_exception_handlers(app) + return app + + +async def test_invalid_webhook_config_returns_client_error() -> None: + app = _create_invalid_webhook_app() + transport = ASGITransport(app=app) + + async with AsyncClient(transport=transport, base_url="http://test") as client: + response = await client.post("/v2/jobs") + + assert response.status_code == 400 + response_json = cast(dict[str, object], response.json()) + error = cast(dict[str, object], response_json["error"]) + assert response_json["success"] is False + assert error["code"] == "INVALID_ARGUMENT" + assert error["message"] == "Invalid webhook URL" + + async def test_get_to_post_only_route_returns_method_not_allowed() -> None: app = _create_post_only_app() transport = ASGITransport(app=app) diff --git a/packages/shared-python/shared/core/exceptions/webhook_exceptions.py b/packages/shared-python/shared/core/exceptions/webhook_exceptions.py index 9e90c9224..2ca14dfbf 100644 --- a/packages/shared-python/shared/core/exceptions/webhook_exceptions.py +++ b/packages/shared-python/shared/core/exceptions/webhook_exceptions.py @@ -38,8 +38,8 @@ def __init__( internal_message=internal_message, user_message=user_message, http_status_code=400, + details=details, ) - self.details = details class WebhookDeliveryException(WebhookException):