I have a chat lambda that stores messages into dynamodb. If the user is not authorized to add the message, I need to return a custom error exception with unique errorType and message to my client.
I have setup my custom error using the documentation at https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-lambda.html
i.e.:
export default class ForbiddenError extends Error {
/**
* ForbiddenError constructor
* @param {string} name
* @param {string} message
*/
constructor(name = null, message = null) {
message = message || 'You are forbidden from performing this action.'
super(message)
this.name = name || 'Forbidden'
}
}
and then I throw the error inside my app via:
throw new ForbiddenError()
When I test my lambda locally, everything works nicely, the code encounters an error. I catch it and call
context.fail(error)
Even when I test my lambda using a testing lambda in the AWS console, I get a beautiful response containing both the message and the error type:
class ForbiddenError extends Error {
constructor(message) {
super(message)
this.name = 'ForbiddenError'
}
}
exports.handler = (event, context, callback) => {
throw new ForbiddenError('You are forbidden from performing this action.')
return context.fail('test');
};
and the error:
{
"errorType": "ForbiddenError",
"errorMessage": "You are forbidden from performing this action.",
"trace": [
"ForbiddenError: You are forbidden from performing this action.",
" at Runtime.exports.handler (/var/task/index.js:9:21)",
" at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
]
}
but when I call my lambda using appsync, suddenly only the message is passed into the error, but the errorType is always the same: Lambda:Unhandled
i.e.:
{
"graphQLErrors": [
{
"path": [
"storeStreamChatMessage"
],
"data": null,
"errorType": "Lambda:Unhandled",
"errorInfo": null,
"locations": [
{
"line": 2,
"column": 3,
"sourceName": null
}
],
"message": "You are forbidden from performing this action."
}
],
"networkError": null,
"message": "GraphQL error: You are forbidden from performing this action."
}
The response mapping template is the same is in the documentation:
ResponseMappingTemplate: |
#if($ctx.error)
$util.error($ctx.error.message, $ctx.error.type)
#end
$util.toJson($context.result)
I was trying to change the $ctx.error.type to $ctx.error.errorType, but then the errorType is returned as "Custom lambda error" and not "Forbidden".
I have tried using both the context.fail() and callback methods from the lambda exports.handler, both work nicely in the console but both only return the message when called from the appsync resolver.
I have tested that the context.fail(error) is indeed called in the lambda and that the exception is caught via .catch() statement before finally calling the context.fail(error)
Even cloudwatch displays the error with the message and errorType when the main lambda is called, so I'm suspecting an error in exepction and context.fail() returning
In the end, I stringified the error and passed it to my clients via the message column. Unfortunately, when I managed to pass the proper errorType via the resolver response mapping template, the returned error started returning "Custom Error" as the return errorType.