How to set up AWS HTTP gateway to pass through errors

Hi there,

I’m handling exceptions in my lambda function as follows:

} catch (err) {
  return callback(err);
}

This is inline with the AWS docs and works great.

The function is configured in serverless.yml like this:

events:
      - http:
          path: diffs
          method: post

My issue is the HTTP gateway is currently swallowing the error and just returning something like this for all errors:

$ curl -v -H "Content-Type: application/json" -d '{"foo":true}' https://xxx.execute-api.us-west-2.amazonaws.com/production/diffs
*   Trying 54.192.146.131...
* TCP_NODELAY set
* Connected to xxx.execute-api.us-west-2.amazonaws.com (54.192.146.131) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.execute-api.us-west-2.amazonaws.com
* Server certificate: Symantec Class 3 Secure Server CA - G4
* Server certificate: VeriSign Class 3 Public Primary Certification Authority - G5
> POST /production/diffs HTTP/1.1
> Host: xxx.execute-api.us-west-2.amazonaws.com
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 12
> 
* upload completely sent off: 12 out of 12 bytes
< HTTP/1.1 502 Bad Gateway
< Content-Type: application/json
< Content-Length: 36
< Connection: keep-alive
< Date: Tue, 05 Sep 2017 18:02:11 GMT
< x-amzn-RequestId: 56d05ce7-9264-11e7-b26d-133036c2a377
< X-Cache: Error from cloudfront
< Via: 1.1 dc7c4fb5024ff022cad1642ec506a6e8.cloudfront.net (CloudFront)
< X-Amz-Cf-Id: Truwps4GtRigA3-XCoOTo86OOat23WdwLNpi1f0NyBfTyS6Cm8gx9A==
< 
* Connection #0 to host xxx.execute-api.us-west-2.amazonaws.com left intact
{"message": "Internal server error"}

I don’t understand why the 500 emitted by Lambda is being transformed into a 502 Bad Gateway and why the error details aren’t passed through in the body.

What I’d like to see is the gateway return a 500 status code and a body with the lambda error stringified, something like this:

{
  "errorMessage": "Malformed input ...",
  "errorType": "Error",
  "stackTrace": [
    "exports.handler (/var/task/index.js:3:14)"
  ]
}

Has anyone set things up to work this way? I’ve seen suggestions to call callback(null, {...}); with the error details as the second argument rather than callback({...});. However, I don’t like this approach because then Lambda thinks the call succeeded and doesn’t register the error within its metrics.

It looks like you’re using Lambda proxy integration in which case errors work just like a successful responses.

For example:

callback(null, {
  statusCode: 500
  body: JSON.Stringify(err);
});

Using callback(err) is a bad response to the API gateway for Lambda proxy integration.

That would work, I’m just concerned if I don’t use callback(err), Lambda believes the function ran successfully and the error won’t show up in its metrics, for example, on the dashboard:

It feels hacky to be using the success callback for errors.

Regardless of how it feels this is how proxy integration works. If you don’t like it you can use the original Lambda integration method instead. Generally speaking Lambda proxy integration is preferred because it’s less complicated to setup and more flexible.

1 Like

Keep in mind there’s a different between HTTP errors (which are up to your application e.g. a 4xx vs a 5xx error) and Lambda errors (which will show up even if you don’t use callback).

1 Like

Ahh, that makes sense @rowanu thanks for the reminder. In that case I’ll update my thinking to treat handled exceptions as a successful invocation and invocation errors as infrastructure issues and unhandled exceptions.