Skip to content

Handle error conditions from Lambda function in Runtime API #1953

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

Merged
4 commits merged into from
Jan 30, 2025

Conversation

ghost
Copy link

@ghost ghost commented Jan 29, 2025

Issue #, if available: DOTNET-7865

Description of changes:
In this PR, we handle error conditions for when the lambda function itself fails. There are 2 main components.

  1. Handling the error in the lambda runtime api.
  2. Handling the error at the api gateway layer

Handling the error in the lambda runtime api

For handling the error at the lambda runtime api layer we need to mimic how lambda functions behave when they fail. The behavior is described here https://docs.aws.amazon.com/lambda/latest/api/API_Invoke.html#API_Invoke_ResponseSyntax. The TLDR version is that they will set the status code to 200, populate the payload with the event.ErrorResponse object and also set the X-Amz-Function-Error header to the event.ErrorType.

image

Handling the lambda error for api gateway

For handling the error at the api gateway layer, it seems that api gateway will just show internal server error when the lambda has an error behind the scenes, so I have done the same.

image

Testing/Validation

Testing lambda runtime api

In order to verify the accuracy of how i am doing the error handling for the lambda runtime api, did the following. I deployed a lambda function that looks like this (it will throw a null pointer exception and fail)

exports.handler = async (event) => {
var x;
return x.toUpper()
};

I then created a console app to invoke the lambda function directly.

var response = await lambdaClient.InvokeAsync(request);

    // Process the response
    if (response.StatusCode == 200)
    {
        Console.WriteLine("Lambda function invoked successfully.");
        string responsePayload = System.Text.Encoding.UTF8.GetString(response.Payload.ToArray());
        Console.WriteLine($"Response payload: {responsePayload}");
    }
    else
    {
        Console.WriteLine($"Error invoking Lambda function. Status code: {response.StatusCode}");
    }

I was able to validate that the response code was 200, the responsePayload contained the same evnt.ErrorResponse object that i populate in my code, and that the X-Amz-Function-Error was the same to the one that i populate in my code. (sample fiddler response below)

HTTP/1.1 200 OK
Date: Wed, 29 Jan 2025 16:53:47 GMT
Content-Type: application/json
Content-Length: 308
Connection: keep-alive

X-Amz-Function-Error: Unhandled

{"errorType":"TypeError","errorMessage":"Cannot read properties of undefined (reading 'toUpper')","trace":["TypeError: Cannot read properties of undefined (reading 'toUpper')","    at exports.handler (/var/task/index.js:3:10)","    at Runtime.handleOnceNonStreaming (file:///var/runtime/index.mjs:1173:29)"]}

Testing api gateway for handling errors

  1. For api gateway, i did similar. I used the same lambda function as above and just hooked up an api gateway and then went to the api gateway url and found that it just returned Internal Server Error.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@@ -137,16 +153,7 @@ private static APIGatewayHttpApiV2ProxyResponse ToHttpApiV2Response(string respo
catch
{
// If deserialization fails, return Internal Server Error
return new APIGatewayHttpApiV2ProxyResponse
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i just put this in its own function so i can re-use it


if (settings.ApiGatewayEmulatorMode.Equals(ApiGatewayEmulatorMode.HttpV2))
{
var lambdaResponse = InvokeResponseExtensions.ToHttpApiV2ErrorResponse();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept these function (ToErrorResponse) inside the extensions class because I wasn't sure else where to put them, even though they really arent being used as extensions. I figured it's fine for now but if anyone has better ideas let me know.

@ghost ghost requested review from normj and philasmar January 29, 2025 17:51
@ghost ghost marked this pull request as ready for review January 30, 2025 14:04
@ghost
Copy link
Author

ghost commented Jan 30, 2025

i tried for a couple of hours to have unit tests for the api gateway process and lambda runtime api service class, but with the way the current classes are written they are difficult to mock.

@ghost ghost added the Release Not Needed Add this label if a PR does not need to be released. label Jan 30, 2025
Copy link
Member

@normj normj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change looks good. Can we get some tests added to the PR?

@ghost
Copy link
Author

ghost commented Jan 30, 2025

@normj i was able to add unit tests for the lambdaruntimeapitests class only. With the way the current ApiGatewayProcess class is written, its not easy to mock/test the stuff that happens in the appl/catchall block. I also figured adding an integration test for just this api gateway behavior was overkill (since it would just be testing that the result is internal server error). let me know your thoughts

@ghost ghost requested a review from normj January 30, 2025 20:30
@ghost ghost force-pushed the gcbeatty/errorconditions branch from aae407c to 7a5cc8b Compare January 30, 2025 20:44
@ghost ghost merged commit 6986f04 into feature/lambdatesttool-v2 Jan 30, 2025
5 checks passed
@ghost ghost deleted the gcbeatty/errorconditions branch January 30, 2025 21:58
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Release Not Needed Add this label if a PR does not need to be released.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants