-
Notifications
You must be signed in to change notification settings - Fork 420
Bug: RequestValidationError handling behavior should not be affected if we add exception handler for Exception #3841
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Thanks for opening your first issue here! We'll come back to you as soon as we can. |
hey @aitchnyu - thank you for submitting an extensive bug report!! It should work with I'll investigate the behaviour today when catching a catch-all As per docs, catching from typing import Optional
import requests
from pydantic import BaseModel, Field
from aws_lambda_powertools import Logger, Tracer
from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response, content_types
from aws_lambda_powertools.event_handler.openapi.exceptions import RequestValidationError
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.utilities.typing import LambdaContext
tracer = Tracer()
logger = Logger()
app = APIGatewayRestResolver(enable_validation=True)
class Todo(BaseModel):
userId: int
id_: Optional[int] = Field(alias="id", default=None)
title: str
completed: bool
@app.exception_handler(RequestValidationError)
def handle_validation_error(ex: RequestValidationError):
logger.error("Request failed validation", path=app.current_event.path, errors=ex.errors())
return Response(
status_code=422,
content_type=content_types.APPLICATION_JSON,
body="Invalid data",
)
@app.post("/todos")
def create_todo(todo: Todo) -> int:
response = requests.post("https://jsonplaceholder.typicode.com/todos", json=todo.dict(by_alias=True))
response.raise_for_status()
return response.json()["id"]
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_HTTP)
@tracer.capture_lambda_handler
def lambda_handler(event: dict, context: LambdaContext) -> dict:
return app.resolve(event, context) ![]() |
Can't reproduce with either general Example below on how to get those exact details what the documentation suggests with from typing import Annotated, Optional
import requests
from aws_lambda_powertools import Logger, Tracer
from aws_lambda_powertools.event_handler import (
APIGatewayRestResolver,
Response,
content_types,
)
from aws_lambda_powertools.event_handler.openapi.exceptions import (
RequestValidationError,
)
from aws_lambda_powertools.event_handler.openapi.params import Path, Query
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools.utilities.parser import Field, BaseModel
tracer = Tracer()
logger = Logger()
app = APIGatewayRestResolver(enable_validation=True)
app.enable_swagger(path="/swagger")
class Todo(BaseModel):
userId: int
id_: Optional[int] = Field(alias="id", default=None)
title: str
completed: bool
@app.exception_handler(RequestValidationError)
def catch_validation(ex: RequestValidationError):
logger.info("Catch all exception handler", error=ex)
err = ex.errors()[0]
location, message, type = err["loc"], err["msg"], err["type"]
return Response(
status_code=400,
content_type=content_types.TEXT_PLAIN,
body=f"Uh oh! Received an exception",
)
@app.get("/todos")
@tracer.capture_method
def get_todos(
completed: Annotated[str | None, Query(min_length=4)] = None
) -> list[Todo]:
url = "https://jsonplaceholder.typicode.com/todos"
if completed is not None:
url = f"{url}/?completed={completed}"
todo = requests.get(url)
todo.raise_for_status()
return todo.json()
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)
@tracer.capture_lambda_handler
def lambda_handler(event: dict, context: LambdaContext) -> dict:
return app.resolve(event, context) |
My workaround is having handlers for both
|
Hi @heitorlessa and @aitchnyu, after investigating further I discovered that the issue goes beyond fixing the Ideally, it's advisable to catch more specific exceptions, especially if you want to generate custom messages for specific error types like codefrom aws_lambda_powertools import Logger, Tracer
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools.event_handler.api_gateway import Response
tracer = Tracer()
logger = Logger()
app = APIGatewayRestResolver()
@app.get("/hello")
def get_todos():
raise ValueError
@app.exception_handler(Exception)
def handle_invalid_limit_qs(ex: Exception): # receives exception raised
return Response(
status_code=400,
body="Catching Exception, but raising ValueError",
)
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)
def lambda_handler(event: dict, context: LambdaContext) -> dict:
return app.resolve(event, context) executionsam local invoke --event events/event.json --skip-pull-image
Invoking app.lambda_handler (python3.11)
Requested to skip pulling images ...
Mounting /home/leandro/DEVEL-PYTHON/tmp/exception-different-router/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated, inside runtime container
START RequestId: e408630c-0660-4d24-a3c5-d6e590f6d959 Version: $LATEST
START RequestId: e408630c-0660-4d24-a3c5-d6e590f6d959 Version: $LATEST
END RequestId: e408630c-0660-4d24-a3c5-d6e590f6d959
REPORT RequestId: e408630c-0660-4d24-a3c5-d6e590f6d959 Init Duration: 0.11 ms Duration: 407.67 ms Billed Duration: 408 ms Memory Size: 128 MB Max Memory Used: 128 MB
{"statusCode": 400, "body": "Catching Exception, but raising ValueError ", "isBase64Encoded": false, "multiValueHeaders": {}} I'm adding labels such as “need help” and “revisit” to keep this issue on our radar. Thank you |
Hey @heitorlessa and @aitchnyu! After a long investigation, we have concluded that this is a fundamental aspect of how Python handles exceptions. In Python, most exceptions inherit from the base class For cases like this, the recommended approach is to catch more specific exceptions, to better handle and respond to different error types. I'm closing this issue and please reopen if you find a solution or have an idea how to address this. Thanks |
|
Expected Behaviour
I have an exception handler for
Exception
.When a validation error happens, I want to get the response:
Current Behaviour
I get the response of my error handler instead of the pydantic errors.
Code snippet
Possible Solution
The code responsible is
My workaround is to have a version of
APIGatewayRestResolver
that avoids returning error handler forException
forRequestValidationError
.I understand my workaround may be against the behavior of #3395 .
If I had to suggest a fix, I would suggest moving validation error from
_call_exception_handler
to its own method, similar to the case of_not_found
handler:Steps to Reproduce
Copy paste of above snippet:
Then trigger a pydantic validation error.
Powertools for AWS Lambda (Python) version
latest
AWS Lambda function runtime
3.8
Packaging format used
PyPi
Debugging logs
No response
The text was updated successfully, but these errors were encountered: