Skip to content
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

Issues accessing response body when API Gateway returns 400 #8806

Closed
3 tasks done
tom-wagner opened this issue Aug 26, 2021 · 11 comments
Closed
3 tasks done

Issues accessing response body when API Gateway returns 400 #8806

tom-wagner opened this issue Aug 26, 2021 · 11 comments
Assignees
Labels
API Related to REST API issues

Comments

@tom-wagner
Copy link

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

REST API

Amplify Categories

api

Environment information

# Put output below this line
npx envinfo --system --binaries --browsers --npmPackages --duplicates --npmGlobalPackages
npx: installed 1 in 0.723s

  System:
    OS: macOS Mojave 10.14.6
    CPU: (8) x64 Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz
    Memory: 1.66 GB / 16.00 GB
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 14.8.0 - ~/.nvm/versions/node/v14.8.0/bin/node
    Yarn: 1.22.10 - /usr/local/bin/yarn
    npm: 6.14.7 - ~/.nvm/versions/node/v14.8.0/bin/npm
  Browsers:
    Chrome: 92.0.4515.159
    Firefox: 78.10.1
    Safari: 14.1.2
  npmPackages:
    @amzn/awsui-components-react-v3: ^3.0.0 => 3.0.0
    @amzn/awsui-design-tokens: ^3.0.0 => 3.0.0
    @amzn/awsui-global-styles: * => 1.0.0
    @amzn/awsui-jest-preset: * => 1.0.2
    @amzn/awsui-test-utils-multi-version: * => 1.0.0
    @amzn/brazil: ^1.1.2 => 1.1.2
    @amzn/sail_boost_event_bus_commons: * => 1.0.0
    @amzn/sail_boost_types: * => 1.0.0
    @amzn/sales-console-components-react: * => 0.0.1
    @babel/core: 7.12.3 => 7.12.3 (7.9.0)
    @babel/eslint-parser: ^7.14.7 => 7.14.7
    @my-scope/package-a:  0.0.0
    @my-scope/package-b:  0.0.0
    @pmmmwh/react-refresh-webpack-plugin: 0.4.2 => 0.4.2
    @reduxjs/toolkit: ^1.5.0 => 1.5.0
    @svgr/webpack: ^5.4.0 => 5.4.0
    @testing-library/jest-dom: ^5.11.5 => 5.11.5
    @testing-library/react: ^11.0.4 => 11.1.0
    @testing-library/user-event: ^12.2.2 => 12.2.2
    @types/lodash.flatten: ^4.4.6 => 4.4.6
    @types/lodash.get: ^4.4.6 => 4.4.6
    @types/lodash.reduce: ^4.6.6 => 4.6.6
    @types/query-string: ^6.3.0 => 6.3.0
    @types/react: ^16.9.56 => 16.9.56
    @types/react-dom: ^16.9.9 => 16.9.9
    @types/react-redux: ^7.1.16 => 7.1.16
    @types/react-router: ^5.1.8 => 5.1.8
    @types/react-router-dom: ^5.1.6 => 5.1.6
    @types/sanitize-html: ^1.27.1 => 1.27.1
    @types/yup: ^0.29.9 => 0.29.9
    @typescript-eslint/eslint-plugin: ^4 => 4.5.0
    @typescript-eslint/parser: ^4 => 4.5.0
    amazon-connect-streams: ^1.6.8 => 1.6.8
    aws-amplify: ^3.3.8 => 3.3.8
    babel-jest: ^26.6.0 => 26.6.1 (25.2.4)
    babel-loader: 8.1.0 => 8.1.0
    babel-plugin-named-asset-import: ^0.3.7 => 0.3.7
    babel-preset-react-app: ^10.0.0 => 10.0.0
    baz:  undefined ()
    bfj: ^7.0.2 => 7.0.2
    browser_field:  undefined ()
    browserslist: ^4.16.3 => 4.16.3 (4.14.2)
    case-sensitive-paths-webpack-plugin: 2.3.0 => 2.3.0
    css-loader: 4.3.0 => 4.3.0
    csstype: 3.0.4 => 3.0.4 (3.0.8)
    dotenv: ^8.2.0 => 8.2.0
    dotenv-expand: ^5.1.0 => 5.1.0
    eslint: ^7.11.0 => 7.12.0
    eslint-config-prettier: 6 => 6.14.0
    eslint-config-react-app: ^6.0.0 => 6.0.0
    eslint-plugin-flowtype: ^5.2.0 => 5.2.0
    eslint-plugin-import: ^2.22.1 => 2.22.1
    eslint-plugin-jest: ^24.1.0 => 24.1.0
    eslint-plugin-jsx-a11y: ^6.3.1 => 6.4.0
    eslint-plugin-react: ^7.21.5 => 7.21.5
    eslint-plugin-react-hooks: ^4.2.0 => 4.2.0
    eslint-plugin-testing-library: ^3.9.2 => 3.9.2
    eslint-webpack-plugin: ^2.1.0 => 2.1.0
    file-loader: 6.1.1 => 6.1.1
    formik: ^2.2.5 => 2.2.5
    fs-extra: ^9.0.1 => 9.0.1 (8.1.0, 7.0.1)
    graphql: ^15.4.0 => 15.4.0 (14.0.0)
    handlebars: ^4.7.6 => 4.7.6
    html-webpack-plugin: 4.5.0 => 4.5.0
    identity-obj-proxy: 3.0.0 => 3.0.0
    invalid main:  undefined ()
    jest: 26.6.0 => 26.6.0
    jest-circus: 26.6.0 => 26.6.0
    jest-watch-typeahead: 0.6.1 => 0.6.1
    lodash.flatten: ^4.4.0 => 4.4.0
    lodash.get: ^4.4.2 => 4.4.2
    lodash.reduce: ^4.6.0 => 4.6.0
    memo-parser:  0.2.1
    merge: ^2.1.0 => 2.1.0
    mini-css-extract-plugin: 0.11.3 => 0.11.3
    monorepo-symlink-test:  0.0.0
    mylib:  0.0.0
    node-sass: ^4.14.1 => 4.14.1
    optimize-css-assets-webpack-plugin: 5.0.4 => 5.0.4
    pnp-webpack-plugin: 1.6.4 => 1.6.4
    postcss-flexbugs-fixes: 4.2.1 => 4.2.1
    postcss-loader: 3.0.0 => 3.0.0
    postcss-normalize: 8.0.1 => 8.0.1
    postcss-preset-env: 6.7.0 => 6.7.0
    postcss-safe-parser: 5.0.2 => 5.0.2
    prettier: 2 => 2.1.2
    query-string: ^6.13.8 => 6.13.8 (4.3.4)
    quill: ^1.3.7 => 1.3.7
    react: ^17.0.1 => 17.0.1
    react-app-polyfill: ^2.0.0 => 2.0.0
    react-dev-utils: ^11.0.0 => 11.0.0
    react-dom: ^17.0.1 => 17.0.1
    react-quill: ^1.3.5 => 1.3.5
    react-redux: ^7.2.2 => 7.2.2
    react-refresh: ^0.8.3 => 0.8.3
    react-router-dom: ^5.2.0 => 5.2.0
    react-string-replace: ^0.4.4 => 0.4.4
    react-tooltip: ^4.2.13 => 4.2.13
    resolve: 1.18.1 => 1.18.1 (1.15.1, 1.20.0)
    resolve-url-loader: ^3.1.2 => 3.1.2
    sanitize-html: ^2.3.2 => 2.3.2
    sass-loader: 8.0.2 => 8.0.2
    semver: 7.3.2 => 7.3.2 (6.3.0, 5.7.1, 7.0.0, 5.3.0)
    style-loader: 1.3.0 => 1.3.0
    terser-webpack-plugin: 4.2.3 => 4.2.3 (1.4.5)
    typescript: 4.0 => 4.0.3
    url-loader: 4.1.1 => 4.1.1
    webpack: 4.44.2 => 4.44.2
    webpack-dev-server: 3.11.0 => 3.11.0
    webpack-manifest-plugin: 2.2.0 => 2.2.0
    workbox-webpack-plugin: 5.1.4 => 5.1.4
    yup: ^0.29.3 => 0.29.3
  npmGlobalPackages:
    aws-cdk: 1.115.0
    npm: 6.14.7
    serverless: 2.52.1

Describe the bug

When calling API.get() on a route with CORS enabled and "Default 4xx" configured in the API Gateway console, the 400 response returns a value in the network tab but there is no data on error.response.data in the catch block. This issue has been discussed ad nauseam around the internet, including here:
#6661

Additionally, Amplify in the docs mentions it is a wrapper on Axios, and the issue is documented on the Axios repo here. It does not seem like the underlying Axios issue has ever been resolved
axios/axios#960

image

image

image

Expected behavior

We would expect to be able to access the error response body via error.response.data.

Reproduction steps

Follow the steps as outlined here:
#6661

Code Snippet

return new Promise((resolve, reject) => {
    API
      .get(
        config.getSparkApiGatewayConfig().name,
        "/usr/uploads",
        {}
      )
      .then(response => {
        debugger;
        resolve(response);
      })
      .catch(error => {
        debugger;
        reject(error);
      });
     // as IListUploadsResponse);
  });

Log output

No response

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

@tom-wagner
Copy link
Author

This also seems to be a very common customer issue per a simple Google search

@jamesaucode jamesaucode added pending-triage Issue is pending triage API Related to REST API issues labels Aug 26, 2021
@chrisbonifacio chrisbonifacio self-assigned this Sep 7, 2021
@tom-wagner
Copy link
Author

Bumping this -- any update?

@hkjpotato
Copy link
Contributor

Screen Shot 2021-09-15 at 6 16 31 PM

I can see my error response when calling a customer HTTP API endpoint. There might be something missing in REST API endpoint setting.

@ynnr85
Copy link

ynnr85 commented Jul 4, 2022

any update?

@ynnr85
Copy link

ynnr85 commented Jul 4, 2022

image
Note it happens on 500 status response also

@iartemiev iartemiev added p3 and removed pending-triage Issue is pending triage labels Jul 18, 2022
@dpilch
Copy link
Member

dpilch commented Jul 19, 2022

I haven't been able to reproduce this error following #6661. @ynnr85 if you have the time, could you find the minimum reproduction so that we can investigate the issue?

@oakideas
Copy link

oakideas commented Sep 19, 2022

Hi @dpilch , I'm having the same behavior in my application. Every time I call an endpoint and the HTTP code of the response is >=300 I just get a message saying the error code (eg Request failed with status code 404) and I don't have access to the payload.

I'll use the post method as an example. based on what I understand reading the code of this library the problem occurs because on line 413 of the RestClient.ts file. (

return axios(signed_params)
) .

		return axios(signed_params)
			.then(response => (isAllResponse ? response : response.data))
			.catch(error => {
				logger.debug(error);
				throw error;
			});

When axios returns an error, the code throws this error to the RestAPI.ts file, line 158.

return Promise.reject(err.message);

	post(apiName, path, init): Promise<any> {
		try {
			const apiInfo = this.getEndpointInfo(apiName, path);

			const cancellableToken = this._api.getCancellableToken();

			const initParams = Object.assign({}, init);
			initParams.cancellableToken = cancellableToken;

			const responsePromise = this._api.post(apiInfo, initParams); //<---- HERE calls RestClient

			this._api.updateRequestToBeCancellable(responsePromise, cancellableToken);

			return responsePromise;
		} catch (err) {
			return Promise.reject(err.message); //<---- HERE 's rejecting the promise sending only the message property. It's why we don't see the the data on response.
		}
	}

I believe a solution would be to return the err object instead of the err.message when the response parameter is true, something like:

return Promise.reject(initParams.isAllResponse ? err : err.message)
(would need some more refactory in the function to work, but it's the idea)

I tested with the latest version of axios it always throws an error when the http code is >= 300, but I saw in the documentation that it is a configurable behavior through the validateStatus function https://axios -http.com/docs/handling_errors.

I used this piece of code to test. if you comment the validateStatus and call a URL that returns http 404 for example, it will fall into the catch block.

const main = async () => {
    axios.post('http://localhost:9000/app/user/login', {}, 
        {
            validateStatus: function (status) {
                return status < 500;
            }
        }
    ).then((data) => {
        console.log('success');
    }).catch((error) => {
        console.log(error.message);
    })
}
main();

it would also be a solution to add this behavior to axios, but I think it has more potential to cause problems for those who already use the lib as it is. (or exposing some way to inject parameters to axios instance, there is also another open issue about it.)

In my scenario the problem is not with CORS, but this unexpected behavior forced me to change my API responses to code 200 and payload information in order to map the error in the application and continue using this component. for example, the way the code is written at the moment, the only way I found to know an 404 error happened would be parsing the string "Request failed with status code 404".

If I can help in any way to solve the problem, let me know.

@shwetajoshi601
Copy link

Hi,
Any update on this issue? We are facing the same problem. The error is visible in the Network tab, but not available through error.response. It is kind of critical as the frontend error handlers cannot display specific error messages.

@dvalbuena1
Copy link

I am using 6.0.9 version and the problem persist. It is related to this other issue #6661.
But I have found a workaround only if you are able to modify the backend and I think I have found the problem in the library.

So when the API response with status code greater than or equal to 300 the following code is executed:

export const parseJsonError: ErrorParser = async (response?: HttpResponse) => {
	if (!response || response.statusCode < 300) {
		return;
	}
	const body = await parseJsonBody(response);
	const sanitizeErrorCode = (rawValue: string | number): string => {
		const [cleanValue] = rawValue.toString().split(/[\,\:]+/);
		if (cleanValue.includes('#')) {
			return cleanValue.split('#')[1];
		}
		return cleanValue;
	};
	const code = sanitizeErrorCode(
		response.headers['x-amzn-errortype'] ??
			body.code ??
			body.__type ??
			'UnknownError'
	);
	const message = body.message ?? body.Message ?? 'Unknown error';
	const error = new Error(message);
	return Object.assign(error, {
		name: code,
		$metadata: parseMetadata(response),
	});
};

parseJsonBody looks like is used to retrieve the body of the response:

export const parseJsonBody = async (response: HttpResponse): Promise<any> => {
	if (!response.body) {
		throw new Error('Missing response payload');
	}
	const output = await response.body.json();
	return Object.assign(output, {
		$metadata: parseMetadata(response),
	});
};

(Both functions are in the same file: packages/core/src/clients/serde/json.ts)

In the parseJsonError function the only keys that are being used from the body are message, code and __type.
This means that the only way to get any feadback from the backend is that the response body has any of these keys.

So the only way I have found to handle the error on the frontend is that the backend response looks like this:

{
  "message": "The error is..."
}

I hope this can be useful to solve the problem and to those who need a temporary solution.

@tuskermanshu
Copy link

tuskermanshu commented Feb 2, 2024

Is there any other way to get the content of the body of the error other than changing the backend code?

@chrisbonifacio
Copy link
Member

We've released a fix addressing this and #12943 with Amplify JS v6.0.15.

Closing this but if you are still experiencing issues related to accessing the body of error responses, please open a new issue!

Also, please refer to our documentation for more info on accessing http responses: https://docs.amplify.aws/react/build-a-backend/restapi/fetch-data/#access-http-response-from-errors

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API Related to REST API issues
Projects
None yet
Development

No branches or pull requests