502 errors on lambda function when deployed

I’m trying to add a lambda function to my Gatsby site, which is deployed to Netlify via Gatsby Cloud. The function automatically sends the user a formatted email when they submit a form - I’m using a custom callback in the form response handler rather than the submission-created.js approach. In netlify dev it works, however when I try it on the deployed site, I get a 502 error:

Here’s the function code:

const sendgrid = require('@sendgrid/mail');

const getFirstName = (name) => {
  const names = name.split(' ');
  return names[0];

function sendEmail(client, applicant, email, senderEmail, senderName) {
  return new Promise((fulfill, reject) => {

    const data = {
      from: {
        email: senderEmail,
        name: senderName,
      subject: 'Application received',
      to: `${email}`,
      html: `Hi ${getFirstName(applicant)},
       Thanks for your interest, we'll be in touch!

      .then(([response]) => {
      .catch((error) => reject(error));

exports.handler = function (event, callback) {
  const applicant = event.queryStringParameters.applicant;
  const email = event.queryStringParameters.email;

  const { SENDGRID_API_KEY } = process.env;


  sendEmail(sendgrid, applicant, email, 'our-email@email.com', 
'Our Name')
    .then((response) => callback(null, { statusCode: response.statusCode }))
    .catch((err) => callback(err, null));


Realised I forgot to make the exports.handler an async function. Have fixed that now, but still getting the 502, and this error when I log the response:

Error: Cannot find module '/var/task/src/node_modules/@sendgrid/mail/index.js'. Please verify that the package.json has a valid "main" entry

Seems to be something to do with installing the function dependencies, however I have setup a package.json in the /functions folder with @sendgrid/mail listed as a dependency and emailApplicant.js (the name of the lambda function) as the main, and added an onPostBuild function in gatsby-node.js to npm install the dependencies as stated here: Deploying Netlify Functions from Gatsby Cloud – Gatsby Cloud

(This is Gatsby specific but might be helpful)

const util = require("util");
const child_process = require("child_process");
const exec = util.promisify(child_process.exec);

exports.onPostBuild = async (gatsbyNodeHelpers) => {
  const { reporter } = gatsbyNodeHelpers;

  const reportOut = (report) => {
    const { stderr, stdout } = report;
    if (stderr) reporter.error(stderr);
    if (stdout) reporter.info(stdout);

  reportOut(await exec("cd public/functions && npm install"));

PS Here is the site: https://602f541336377987ffe38b0d--studiomass.netlify.app/careers

Submitting one of the forms triggers the function

Hey there, @StudioMASS :wave:

Thanks so much for reaching out! Sorry for the delay in response. This is a bit outside the scope of my expertise, but I am going to loop in some folks who may be able to support you here!

@jonsully , I know that you have experience with lambda functions as well as @AaronP . Can you two take a look here and see if you can identify some resources or next steps?


Hey @StudioMASS! :wave:t2:

And welcome to The Forums :netliheart:

Two things I’m seeing off the bat here — first, within your handler function (which you did need to make async so +1 for that) you’ll need to use await before calling sendEmail(). Otherwise the call to sendEmail() will be considered an async call that gets put onto the call stack but never called once the execution of the handler function / stack ends. That’s one of the realities of using AWS Lambda so not much we can do there — Lambda does not continue to process function calls on the call stack beyond handler itself once handler ends. You must use await for any async function you want to run during your function. :+1:t2:

Second, in terms of build / dependency stuff, can you try the approach of a second nested folder for each function name? This looks like this:

└── project-name/
    ├── src (your actual static site)/
    │   ├── js
    │   ├── css
    │   └── html/
    │       ├── index.html
    │       └── etc
    └── functions/
        ├── my-function-name/
        │   ├── index.js
        │   └── package.json
        └── my-second-function/
            ├── index.js
            └── package.json

It’s just an extra nested folder (folder name == function name, js file is just “index”) so that each function gets their own package.json for more isolated dependencies. Netlify should automatically resolve and package those dependencies if you follow this pattern.

Let me know if that helps!