Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AWS Amplify returns generic Network Error on any Lambda failure

I'm building an app that connects with a serverless API on AWS (API Gateway/Lambda) via AWS Amplify used on a web (React) frontend.

When the request is successful, everything works just fine (definitionally, I suppose). There are no CORS issues or anything. But if I try to return a "failed" response, my frontend does not receive that response. It receives a generic Network Error with no information about what happened, and the console logs a failure.

I think the code will make more sense of this. I request to the API using code like this (Amplify configured elsewhere):

import { API } from 'aws-amplify';
...
API.get('apiName', path, options)
  .then(response => {
     // Whatever
  })
  .catch(err => {
     // Whatever
  })

In the Lambda, I'm returning (and Cloudwatch verifies this, as I log right before running callback(null, responseObject)) a full response object like this:

{
    "statusCode": 200,
    "headers": {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Credentials": true
    },
    "body": "{ /* whatever */ }"
}

or this:

{
    "statusCode": 500,
    "headers": {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Credentials": true
    },
    "body": "{ /* error here */ }"
}

In the 200 case, the response reaches my frontend perfectly (shows up in the .then function as response). In the 500 (or general error) case, the .catch is called and the err object is not what I put in the response body, but rather a generic Network Error with no relevant information (or status code):

Error: Network Error
    at createError (createError.js:16)
    at XMLHttpRequest.handleError (xhr.js:87)

To clarify, simply changing the statusCode from 200 to 400 both means the request is caught on the frontend (good), and that the error has none of the information I'm returning from the Lambda (bad).

The console also logs errors:

DELETE https://abcdefg.execute-api.us-west-2.amazonaws.com/development/my-endpoint 403 ()

Failed to load https://abcdefg.execute-api.us-west-2.amazonaws.com/development/my-endpoint: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 403.

It also logs stuff related to a CORS failure that makes no sense to me, because I'm following the exact same pattern as for success cases, just with a higher statusCode.

Cross-Origin Read Blocking (CORB) blocked cross-origin response https://abcdefg.execute-api.us-west-2.amazonaws.com/development/my-endpoint with MIME type application/json.
XHR failed loading: DELETE "https://abcdefg.execute-api.us-west-2.amazonaws.com/development/my-endpoint".

Any idea what's going on here? It's really frustrating not to have access to the real informative error on the frontend. Please let me know if there's any other information that would be helpful for solving this issue.

EDIT -- following @Michael's feedback below, I checked to see if my app was using lambda or lambda-proxy, and it is, in fact, using lambda-proxy, which should not produce this behavior, as I understand it.

Here's some code in my serverless.yml file,

functions:
  projectsGetAll:
    handler: handlers/projects/getAll.handler
    events:
      - http:
          path: projects
          method: get
          cors: true
          authorizer: aws_iam

And here's a screenshot (with the endpoint whited out) from AWS API Gateway for one of these endpoints:

enter image description here

I want to confirm that this is, then, unexpected behavior for lambda-proxy. Is there possibly some setting that is mucking things up?

like image 500
Sasha Avatar asked Aug 24 '18 18:08

Sasha


Video Answer


2 Answers

Figured it out! The error object coming back (a generic 500 and NetworkError contained my constructed error, under the response key).

So if I log error.response, I get my fully constructed error object, with its own message and statusCode, even though the error object itself has a code of 500 and message of Error: Network Error, and the error catching process seems to throw out inaccurate error logs (CORB stuff) in the console.

Weird. Not sure if that's expected Lambda behavior, or something with Amplify, or something I'm doing wrong, but if anyone else runs into this issue, I hope this helps!

EDIT - Apparently that is expected Amplify behavior, which I missed in the docs.

But I figured out another issue that seems to be a bug. This response key only shows up when I don't specify headers in the request options. In other words, this request returns an error with a response key:

API.get('my-api', '/path')

But this one returns an error without the key:

API.get('my-api', '/path', { headers: { 'Content-Type': 'application/json' } })

Very weird, but true for me. Even other options are fine, but the headers option seems to mess things up (and only for the error response).

like image 163
Sasha Avatar answered Sep 28 '22 10:09

Sasha


This does end up depending on how you configure your API Gateway, e.g. Lambda proxy etc. If you are NOT using a Lambda proxy setup, you will need to map these error responses/codes within API Gateway, see extensive post on that here: https://aws.amazon.com/blogs/compute/error-handling-patterns-in-amazon-api-gateway-and-aws-lambda/

If you are using a Lambda Proxy, you will just return as a specific type of proxy response from Lambda: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html

like image 33
Michael Avatar answered Sep 28 '22 10:09

Michael