'Temporary directory in node not working randomly. Works sometimes but not others

I am using GCP functions in a Firebase web app. I am having trouble accessing the tmp directory properly. What I am doing incorrectly? (The odd thing is that the code seems to also work randomly.)

const tmp = os.tmpdir();
const PdfPrinter = require('pdfmake');
const fs = require('fs');

const tmp = os.tmpdir()
const dd = [] 
const pdfDoc = printer.createPdfKitDocument(dd);
pdfDoc.pipe(fs.createWriteStream(`${tmp}/relative.pdf`));
pdfDoc.end();

const attachment = fs.readFileSync(`${tmp}/relative.pdf`).toString("base64"); // error here

Error:

Error: ENOENT: no such file or directory, open '/tmp/relative.pdf'
    at Object.openSync (node:fs:585:3)
    at Object.readFileSync (node:fs:453:35)
    at /workspace/index.js:494:27
    at processTicksAndRejections (node:internal/process/task_queues:96:5) 


Solution 1:[1]

Here is the answer I ended up going with. I had two attachments which I needed to wait until they were complete.

I ended up switching to using Buffer, but I think I could have used the tmp directory and it would have worked. Essentially, the function was just closing down before it was finished running. So, I set up some checks to make sure everything was finished.

    const docDefinition = {
        // normal doc definition
    }
    const pdfDoc = printer.createPdfKitDocument(docDefinition);
    const pdfTitle = workOrder.replace(/[^a-zA-Z0-9 ]/g, '')

    pdfDoc.on('data', (chunk) => {
      chunks.push(chunk);
    });
    let attachment
    pdfDoc.on('end', () => {
      const result = Buffer.concat(chunks);
      attachment = {
        content: result.toString('base64'),
        filename: `Estimate-${pdfTitle}.pdf`,
        type: "application/pdf",
        disposition: "attachment"
      }
    });

    pdfDoc.on('error', (err) => {
      console.log(err);
    });

    pdfDoc.end();

    const printer_2 = new Printer(fontDescriptors);
    const chunks_2 = [];
    const docDefinition_2 = {
        // normal doc definition
    }
    const pdfDoc_2 = printer_2.createPdfKitDocument(docDefinition_2);

    pdfDoc_2.on('data', (chunk) => {
      chunks_2.push(chunk);
    });

    let attachment_2
    pdfDoc_2.on('end', () => {
      const result_2 = Buffer.concat(chunks_2)
      attachment_2 = {
        content: result_2.toString('base64'),
        filename: `Lumber-order-${pdfTitle}.pdf`,
        type: "application/pdf",
        disposition: "attachment"
      }
    });

    pdfDoc_2.on('error', (err) => {
      console.log(err);
    });
    pdfDoc_2.end();
    functions.logger.log('Log Before Interval Start')


This chunk of code makes sure that the function doesn't terminate before the two attachments are ready.

    async function waitUntil() {
      return await new Promise(resolve => {
        const interval = setInterval(() => {
          if (attachment && attachment_2) {
            clearInterval(interval)
            functions.logger.log('Attachments Ready')
            let attachments = []
            attachments.push(attachment)
            attachments.push(attachment_2)

            const msg = {
              // to, from, etc.. here
              attachments: attachments
            };
            resolve(msg)
          }
        }, 1000)
      })
    }

    const msg = await waitUntil()
    const resp = await sgMail.send(msg)

Solution 2:[2]

I had a very similar issue. I created a xlsx file with the framework excel4node and then I wanted to read it, to mail it as an attachment. I got the same error as you trying to read the file. Mine also worked randomly.

I could not find a reasonable solution, but adding a small delay before reading the file seems to solve it.

// Function that creates the file and stores it in the /tmp-folder
await createExcelFile() 

// This solved it for me, add a small delay
await new Promise(f => setTimeout(f, 1000));

// Read the file again from tmp-folder
const pathToAttachment = "/tmp/" + reportFileName;
const attachment = fs.readFileSync(pathToAttachment).toString("base64"); 

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 Jordan
Solution 2 Sunkas