'Why am I only getting Mailgun.js error in Cloud Run?

I'm trying to send an email using Mailgun's npm client - Mailgun.js.

When sending in development mode, everything works correctly. But when I upload the Node server to Cloud Run, something breaks.

Here is the code in the sendEmail helper file:

import formData from 'form-data';
import Mailgun from 'mailgun.js';

const sendEmail = async ({ to, subject, text, attachment, scheduledDate }) => {
  const mailgun = new Mailgun(formData);
  const mg = mailgun.client({
    username: 'api',
    key: process.env.MAILGUN_KEY,
    url: 'https://api.eu.mailgun.net'
  });

  const data = {
    from: `<[email protected]>`,
    to,
    subject,
    text
  };

  if (attachment) {
    data.attachment = attachment;
  }

  if (scheduledDate) {
    data['o:deliverytime'] = new Date(scheduledDate).toUTCString();
  }
  
  try {
    const result = await mg.messages.create(process.env.MAILGUN_DOMAIN, data);

    if (result.status && result.status !== 200) {
      throw ({ code: result.status, message: result.message });
    }

    return true;
  } catch(err) {
    console.log(err);
    return { error: err };
  }
};

export default sendEmail;

And then in another file:

import { Router } from 'express';
import generateInvoicePDF from '../helpers/generateInvoicePDF.js';
import sendEmail from '../helpers/sendEmail.js';

const router = Router();

router.post('/email', async (req, res, next) => {
  try {
    const file = await generateInvoicePDF(invoice);

    if (file?.error) {
      throw ({ code: pdf.error.code, message: pdf.error.message });
    }

    const email = await sendEmail({
      to: '[email protected]',
      subject: 'Invoice',
      text: 'Test',
      attachment: { filename: 'Invoice', data: file }
    });
    
    if (email?.error) {
      throw ({ code: email.error.code, message: email.error.message });
    }

    res.status(200).json({ success: true });
  } catch(err) {
    next(err);
  }
});

export default router;

The error I get when in production mode in Cloud Run's logs is:

TypeError: fetch failed 
at Object.processResponse (node:internal/deps/undici/undici:5575:34) 
at node:internal/deps/undici/undici:5901:42 
at node:internal/process/task_queues:140:7 
at AsyncResource.runInAsyncScope (node:async_hooks:202:9) 
at AsyncResource.runMicrotask (node:internal/process/task_queues:137:8) {
  cause: TypeError: object2 is not iterable 
  at action (node:internal/deps/undici/undici:1661:39) 
  at action.next (<anonymous>)
  at Object.pull (node:internal/deps/undici/undici:1709:52)
  at ensureIsPromise (node:internal/webstreams/util:172:19)
  at readableStreamDefaultControllerCallPullIfNeeded (node:internal/webstreams/readablestream:1884:5)
  at node:internal/webstreams/readablestream:1974:7
}

Why the hell does it work in development mode on my local machine, but not when uploaded to Cloud Run?



Solution 1:[1]

For anyone struggling with something similar - I eventually figured out the problem.

On my local machine, where everything was working as expected, I'm using Node v16.15.0, whereas in the Dockerfile, I had specified

FROM node:latest

and therefore Cloud Run was using a newer version, which led to the problems...

I've now deployed using version 16.15.0 and everything works fine

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 CentoKili53