Puppeteer in vercel server not working

My project is using puppeteer to generate screenshots of website and also some other endpoints use it for scraping. It works perfectly fine but suddenly it stopped working showing errors below

image

I tried remaking the screenshot generator from this article from vercel Deploying Puppeteer with Next.js on Vercel but this one it doesnt work too and showing those errors. What change? or what am I doing wrong cause I literally followed the steps and copy pasted the code there in sample but that one does not work too

1 Like

I’ve replicated the issue following the blog post, I’ll check in with the team to see if we can get this fixed

1 Like

I’m not fully clear as to what changed in our runtime to make this example not work, it could be related to some supply chain security changes to stop installed binaries from executing. I’ll continue investigating and if we can restore the functionality of the old way without compromising security I’ll push for that, otherwise we’ll update the guide with the new solution

In the meantime you can reference (or copy) this example repo for how to provision puppeteer that works today

  • use a postinstall script in your package.json that zips chromium and puts in the /public directory
  • in your route handler, instantiate chromium-min with the remote path to the zip and execute it from there
1 Like

Hi guys, I ran into the same persistent issue many have mentioned:

:cross_mark: “The input directory /var/task/node_modules/@sparticuz/chromium/bin does not exist.”

After days of testing every suggestion (including @sparticuz/chromium, playwright-core, custom vercel.json configs, and the manual postinstall zip script, this is what finally worked for my production setup on Vercel.

My Stack:

  1. Next.js 16 (App Router)
  2. Supabase (Postgres + RLS multi-tenant setup)
  3. Vercel Functions (Node.js 20 runtime)
  4. puppeteer-core @ latest
  5. @sparticuz/chromium-min @ latest (141.0.0)
  6. Target: Generate PDFs server-side using Puppeteer (not screenshots)

Solution that worked for me:

  • Install the latest versions npm install puppeteer-core @sparticuz/chromium-min
  • Import these at the top-level of your API route
// app/api/generate-quote-pdf/route.ts
import "puppeteer-core";
import "@sparticuz/chromium-min";
  • Add this to your next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  serverExternalPackages: ["puppeteer-core", "@sparticuz/chromium-min"],
};

export default nextConfig;
  • Create your PDF builder file
// server/pdf/buildQuotePDF.cjs
const puppeteerCore = require("puppeteer-core");
const chromium = require("@sparticuz/chromium-min");
const React = require("react");
const ReactDOMServer = require("react-dom/server");
const { QuotePDFTemplate } = require("../../components/pdf/QuotePDFTemplate");

async function getBrowser() {
  const REMOTE_PATH = process.env.CHROMIUM_REMOTE_EXEC_PATH;
  const LOCAL_PATH = process.env.CHROMIUM_LOCAL_EXEC_PATH;

  if (!REMOTE_PATH && !LOCAL_PATH) {
    throw new Error("Missing a path for Chromium executable");
  }

  if (REMOTE_PATH) {
    // ✅ Use the remote tarball (for Vercel)
    return await puppeteerCore.launch({
      args: chromium.args,
      executablePath: await chromium.executablePath(REMOTE_PATH),
      defaultViewport: chromium.defaultViewport,
      headless: chromium.headless,
    });
  }

  // ✅ Local fallback (for dev)
  return await puppeteerCore.launch({
    executablePath: LOCAL_PATH,
    headless: true,
  });
}

async function buildQuotePDF(quote) {
  let browser;
  try {
    const html = ReactDOMServer.renderToStaticMarkup(
      React.createElement(QuotePDFTemplate, { quote })
    );

    browser = await getBrowser();
    const page = await browser.newPage();
    await page.setContent(html, { waitUntil: "networkidle0" });

    const pdf = await page.pdf({
      format: "A4",
      printBackground: true,
      margin: { top: "0mm", bottom: "10mm" },
    });

    return pdf;
  } catch (error) {
    console.error("❌ Error generating PDF:", error);
    throw error;
  } finally {
    if (browser) await browser.close();
  }
}

module.exports = { buildQuotePDF };

  • Add these environment variables

In Vercel → Settings → Environment Variables

CHROMIUM_REMOTE_EXEC_PATH=https://github.com/Sparticuz/chromium/releases/download/v141.0.0/chromium-v141.0.0-pack.tar.br

In .env.local (for local testing)

CHROMIUM_LOCAL_EXEC_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome

The results I’ve got with my setup:

  • It works perfectly fine on localhost via api
  • Works in production on Vercel so no more missing binary errors