amplify-js
amplify-js copied to clipboard
Issues accessing response body when API Gateway returns 400
Before opening, please confirm:
- [X] I have searched for duplicate or closed issues and discussions.
- [X] I have read the guide for submitting bug reports.
- [X] I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
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:
https://github.com/aws-amplify/amplify-js/issues/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 https://github.com/axios/axios/issues/960
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: https://github.com/aws-amplify/amplify-js/issues/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
This also seems to be a very common customer issue per a simple Google search
Bumping this -- any update?
I can see my error response when calling a customer HTTP API endpoint. There might be something missing in REST API endpoint setting.
any update?
Note it happens on 500 status response also
I haven't been able to reproduce this error following https://github.com/aws-amplify/amplify-js/issues/6661. @ynnr85 if you have the time, could you find the minimum reproduction so that we can investigate the issue?
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. (https://github.com/aws-amplify/amplify-js/blob/41ab57a854676813c3f9d89d647a8ac8f7b2c65f/packages/api-rest/src/RestClient.ts#L413) .
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. https://github.com/aws-amplify/amplify-js/blob/41ab57a854676813c3f9d89d647a8ac8f7b2c65f/packages/api-rest/src/RestAPI.ts#L158
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.
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.
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.
Is there any other way to get the content of the body of the error other than changing the backend code?
We've released a fix addressing this and https://github.com/aws-amplify/amplify-js/issues/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