I have a Lambda function handler that uses several asynchronous operations, such as connecting to a database and a RESTful API for data. When running the function using serverless-offline, I get a warning message in the console saying:
Warning: handler '{function_name}' returned a promise and also use a callback!
This is problematic and might cause issues in you lambda.
I've searched my code, and I don't see any obvious issues with it. I also deployed it to AWS, and it works fine there with no warnings. I've searched this repo for that warning message and found it in the src/createAuthScheme.js and src/index.js files, but I'm not clear how it's making this determination. I haven't found anything in the documentation about this message, either.
Can someone shed some light on the warning and what we should do about it, please? Thank you.
I will share my experience with this.
One developer came to me with the same problem, his method was:
exports.list = async (event) => {
const params = {
TableName: process.env.DYNAMODB_TABLE,
};
// fetch all cities from the database
dynamodb.scan(params, (error, result) => {
// handle potential errors
if (error) {
return {
statusCode: error.statusCode || 501,
headers: { 'Content-Type': 'text/plain' },
body: 'Couldn\'t fetch cities.',
};
}
// create a response
return {
statusCode: 200,
body: JSON.stringify(result),
};
});
};
This method works fine in deployment, but it does not return the right way, so I changed for him:
exports.list = async (event) => {
const params = {
TableName: process.env.DYNAMODB_TABLE,
Limit: 100,
};
const dynamoPromise = dynamodb.scan(params).promise();
return await dynamoPromise.then((response) => {
if (response !== undefined && response.Count > 0) {
return {
statusCode: 200,
body: JSON.stringify(response),
};
} else {
return {
statusCode: 200,
body: '{}',
};
}
});
};
Now it works fine, you see in the first method that it does not return anything (and it should)
@dmelosantos Thanks for the response. Looking at your code samples, I see why the first version wasn't working and why your change fixed it. In my case, I'm using the async/await pattern with webpack and babel prior to running locally. I'm thinking that there may be something in the way it's being transpiled that's causing this error. Oddly, I'm using the same pattern in another function in my service and NOT getting the warning. I'll keep looking at it and see if I can figure out why my code causes it.
In this issue, I was more interested in the thinking behind and the causes of the warning as I couldn't find documentation on the message.
I just encountered the same issue and so had a play around with the code and found that if you just return the response there is no need to pass in and call the callback.
Code causing warning to appear (I'm assuming that your code is similar):
import { getAllRecords } from '../services/database'
export default async (event, context, callback) => {
const records = await getAllRecords()
const response = {
statusCode: 200,
body: JSON.stringify(records)
}
callback(null, response)
}
Code with the callback removed that doesn't trigger the warning:
import { getAllRecords } from '../services/database'
export default async () => {
const records = await getAllRecords()
return {
statusCode: 200,
body: JSON.stringify(records)
}
}
I was surprised to discover that just returning the response object works the same as calling the callback with the response object but seems to work fine.
Hope that is helpful.
@dmelosantos I'm only new to using async/awaits but I think that your second example could be written more in the spirit of the pattern if you didn't use the .then (as that is still using the promise pattern)
So that method could be rewritten as:
exports.list = async (event) => {
const params = {
TableName: process.env.DYNAMODB_TABLE,
Limit: 100,
};
const response = await dynamodb.scan(params).promise();
if (response !== undefined && response.Count > 0) {
return {
statusCode: 200,
body: JSON.stringify(response),
};
} else {
return {
statusCode: 200,
body: '{}',
};
}
};
Thank you for helping me understand, @iaindurham. Your explanation made the solution obvious. :+1:
@pflugs30 just as a follow up to this incase you are having the same issue.
When deploying to AWS returning the await i.e. not calling the callback, didn't work. The lambda would execute but I would get a 502 bad gateway response from CloudFront.
The reason for this is that it doesn't look like it is supported in serverless yet:
However, if you want to implement a Lambda handler in AWS via async/await (i.e. you return the await itself) there is a PR open right now in Serverless to add that functionality. Right now the very top lambda handler only can use the callback with invoke local. Internally you should be able to use async/await at will.
https://github.com/serverless-heaven/serverless-webpack/issues/390#issuecomment-388618175
The PR referenced in that comment appears to be https://github.com/serverless/serverless/pull/5088 which has some other associated PRs that are also waiting being merged.
So until those changes are released I am converting back to calling the callback and just ignoring this warning for the time being
@iaindurham, thanks for the follow up. I should probably mention that I was using babel to transpile my function during webpack before publishing to Lambda. I haven't tried what you describe above, but I'm glad you posted your findings!
I'm using apollo-server-lambda and to be able to modify the headers I need a filter function that calls the callback, as it would in this case to disappear that annoying warnin, any ideas?.
greetings thank you
module.exports.graphql = async function (event, context, callback) {
context.callbackWaitsForEmptyEventLoop = false
const callbackFilter = function (error, output, cb) {
output.headers['Access-Control-Allow-Origin'] = '*'
output.headers['Access-Control-Allow-Credentials'] = true
callback(error, output)
}
const validate = await authenticate(event.headers)
if (!validate.success) {
return {
statusCode: 401,
body: JSON.stringify(validate)
}
}
return graphqlLambda({
schema: GraphQLSchema,
context: {
loaders: createLoaders()
}
})(event, context, callbackFilter)
}
Most helpful comment
I just encountered the same issue and so had a play around with the code and found that if you just return the response there is no need to pass in and call the
callback.Code causing warning to appear (I'm assuming that your code is similar):
Code with the callback removed that doesn't trigger the warning:
I was surprised to discover that just returning the response object works the same as calling the callback with the response object but seems to work fine.
Hope that is helpful.