Puppeteer TimeoutError: истекло время ожидания после 30000 мс

Я пытаюсь добиться очень простой генерации PDF-файлов с помощью Express JS, puppeteer и ejs. Я использовал следующий код несколько раз и достиг аналогичных результатов.

const router = require('express').Router();
const puppeteer = require('puppeteer');
const {_generateHtml} = require("./utils");

router.post('/offer-letter', async (req, res) => {
    const browser = await puppeteer.launch({headless: true});
    const page = await browser.newPage();
    const html = await _generateHtml({
        doj: req.body.doj,
        name: req.body.name,
        role: req.body.role,
    }, 'offer-template.ejs');
    await page.setContent(html, {waitUntil: 'load', timeout: 0});
    const pdfBuffer = await page.pdf({
        printBackground: true,
    });

    res.set('Content-Type', 'application/pdf');
    res.send(pdfBuffer);

});

module.exports = router;

Вот также функция генерацииHtml

const {compile} = require("ejs");
const fs = require("fs");

async function _generateHtml(data, templatePath) {
    const template = fs.readFileSync(templatePath, 'utf8');
    let html = compile(template);
    return html(data);
}

module.exports = {
    _generateHtml
}

Вот структура папок

.env
index.js
node_modules
offer-letter-template-html.html
offer-template.ejs
package-lock.json
package.json
pdfRoutes.js
utils

Однако теперь я обнаружил сообщение об ошибке, которое просто поражает голову. Как именно вы это решаете?

            throw new Errors_js_1.TimeoutError(`Timed out after waiting ${ms}ms`, { cause });
                  ^

TimeoutError: Timed out after waiting 30000ms
    at C:\work\offer-letter-manager\backend\node_modules\puppeteer-core\lib\cjs\puppeteer\common\util.js:289:19
    at C:\work\offer-letter-manager\backend\node_modules\puppeteer-core\lib\cjs\third_party\rxjs\rxjs.js:3986:35
    at OperatorSubscriber2._this._next (C:\work\offer-letter-manager\backend\node_modules\puppeteer-core\lib\cjs\third_party\rxjs\rxjs.js:1055:13)
    at Subscriber2.next (C:\work\offer-letter-manager\backend\node_modules\puppeteer-core\lib\cjs\third_party\rxjs\rxjs.js:678:16)
    at AsyncAction2.<anonymous> (C:\work\offer-letter-manager\backend\node_modules\puppeteer-core\lib\cjs\third_party\rxjs\rxjs.js:4863:24)
    at AsyncAction2._execute (C:\work\offer-letter-manager\backend\node_modules\puppeteer-core\lib\cjs\third_party\rxjs\rxjs.js:1974:16)
    at AsyncAction2.execute (C:\work\offer-letter-manager\backend\node_modules\puppeteer-core\lib\cjs\third_party\rxjs\rxjs.js:1963:26)
    at AsyncScheduler2.flush (C:\work\offer-letter-manager\backend\node_modules\puppeteer-core\lib\cjs\third_party\rxjs\rxjs.js:2235:30)
    at listOnTimeout (node:internal/timers:573:17)
    at process.processTimers (node:internal/timers:514:7) {
  [cause]: undefined
}

Node.js v20.12.1

Вот образец файла ejs, который вы можете использовать

<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>

<div id = "page-container">
    test
</div>


</body>
</html>

22 мая 2024 г. — Редактировать --------------------------------

Это минимальный код ошибки для всех, кто пытается воспроизвести проблему.  

const puppeteer = require("puppeteer");

async function generatePDF() {
  const browser = await puppeteer.launch({headless: true, timeout:0});
  const page = await browser.newPage();
  await page.setContent("<h1>Hello World</h1>", { waitUntil: 'domcontentloaded' });
  console.info("Content set");
  const pdfBuffer = await page.pdf({ printBackground: true });
  console.info("PDF generated");
  await browser.close();
}

generatePDF().catch(console.error);

package.json для проекта

{
  "name": "backend",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "puppeteer": "^22.9.0"
  }
}
Никогда не используйте таймаут: 0. Это подавляет полезные ошибки и приводит к вечному зависанию сценариев. Время ожидания какой строки истекает? Код здесь выглядит более или менее нормально. Можете ли вы включить минимальную версию offer-template.ejs, чтобы я мог запустить код и увидеть ошибку? Я предполагаю, что там нет ничего существенного и это явно не причина зависания, но пример должен быть полным и легко запускаемым. На самом деле, я бы полностью удалил EJS, поскольку он почти наверняка не является частью проблемы.
ggorlen 20.05.2024 23:52

Пока что я отладил, проблема заключается в page.pdf(). Я получаю ту же ошибку даже при использовании await page.setContent(html); вместо await page.setContent(html, {waitUntil: 'load', timeout: 0});.

thedomguy 21.05.2024 00:27

Приятно знать, но информации все еще недостаточно, чтобы воспроизвести это. Пожалуйста, ответьте на все части моего комментария выше. Вы можете отредактировать свое сообщение, включив в него минимально воспроизводимый пример.

ggorlen 21.05.2024 00:32

@ggorlen добавил в сообщение образец файла ejs.

thedomguy 21.05.2024 00:34

Спасибо, но, как показывает мой ответ, это все еще невозможно воспроизвести.

ggorlen 21.05.2024 00:44
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
1 323
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Я не могу воспроизвести вашу проблему. Вот мой код, который отлично работает на Node 20 и Ubuntu 22.04.

сервер.js:

// Note: error handling is not included. I don't recommend this setup;
// it's purely for reproduction and doesn't follow best practices.

const fs = require("fs");
const {compile} = require("ejs"); // ^3.1.10
const express = require("express"); // ^4.19.2
const puppeteer = require("puppeteer"); // ^22.7.1

const app = express();

async function _generateHtml(data, templatePath) {
  const template = fs.readFileSync(templatePath, "utf8");
  const html = compile(template);
  return html(data);
}

app.get("/", async (req, res) => {
  const browser = await puppeteer.launch({headless: true});
  const page = await browser.newPage();
  const html = await _generateHtml({}, "offer-template.ejs");
  await page.setContent(html);
  const pdfBuffer = await page.pdf({
    printBackground: true,
  });
  res.set("Content-Type", "application/pdf");
  res.send(pdfBuffer);

  // browser should be closed in a finally
  // block here if this were a real script.
});

app.listen(3000);

предложение-шаблон.ejs:

<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>

<div id = "page-container">
    test
</div>


</body>
</html>

Запустите node server, зайдите в свой браузер по адресу http://localhost:3000 и вы увидите, что PDF-файл отображается правильно. Поскольку данные POST не используются в шаблоне (и шаблоны не должны иметь значения с точки зрения зависания — в конечном итоге они возвращают строку, которую вы можете жестко запрограммировать для минимальной воспроизводимости), я удалил это. То же самое и с экспортированным модулем — это не имеет значения.

Пожалуйста, предоставьте более подробную информацию и шаги, чтобы воспроизвести проблему. В приведенном вами коде нет ничего явно неправильного, кроме использования таймаута: 0, который подавляет ошибки и может вызвать бесконечное зависание.

спасибо за быстрые ответы чувак. Мне нужно спрятать кровать, так как я буквально падаю со стула, учитывая, что сейчас 5 утра, я первым делом попробую это и вернусь с более воспроизводимым кодом.

thedomguy 21.05.2024 00:55

Звучит отлично. Возможно, вы захотите попробовать запустить мой код здесь в чистой среде и убедиться, что он работает для вас (пожалуйста, подтвердите тот или иной способ, когда сможете). Если этот код у вас не работает, то это интересно — возможно, это какая-то деталь среды.

ggorlen 21.05.2024 00:56

Как ни странно, при использовании именно того кода, которым вы поделились, я все еще получаю ту же ошибку. Я использую узел 20 на Windows 11 с ryzen 7840HS. Вот файл package.json { "name": "backend", "version": "1.0.0", "main": "index.js", "scripts": { "start": "node index.js", "dev": "nodemon index.js" }, "keywords": [], "author": "", "license": "ISC", "description": "", "dependencies": { "cors": "^2.8.5", "ejs": "^3.1.10", "express": "^4.19.2", "puppeteer": "^22.9.0" }, "devDependencies": { "nodemon": "^3.1.0" } }

thedomguy 21.05.2024 20:33

Можете ли вы минимизировать проблему еще больше? У вас должна быть возможность обновить свой пост, просто запустив, создав страницу и вызвав page.pdf() в качестве минимального репрекса — удалите отвлекающие факторы, такие как EJS, Express и Nodemon, которые, вероятно, не связаны с проблемой. Я не уверен, в чем причина, но это может быть ошибка в Puppeteer. Включите в обновление файл package.json вместе с полным работоспособным минимальным ошибочным кодом.

ggorlen 21.05.2024 20:35

Если это происходит только в Windows и Chrome 125+, возможно, это проблема pptr.dev/… (проверьте с помощью dumpio: true)

Alex Rudenko 21.05.2024 20:48

@ggorlen добавил, что это, похоже, помогло. args: ['--no-sandbox', '--disable-setuid-sandbox'] Спасибо за ваши идеи. Я свел его к простому сценарию узла, а затем перешел к нему оттуда.

thedomguy 21.05.2024 21:27

Пожалуйста, обновите сообщение, указав минимальный ошибочный код, чтобы помочь будущим посетителям, и опубликуйте самоответ. Я не думаю, что отключение песочницы — хорошая идея или решение, но рад, что у вас все заработало.

ggorlen 21.05.2024 21:32

Запуск экземпляра puppeteer с отключением песочницы на данный момент работал для меня как обходной путь. В ближайшее время мне придется развернуть фактическую реализацию этого в производстве, поэтому я постараюсь опубликовать более подходящее решение позже.

const browser = await puppeteer.launch({headless: true, args: ['--no-sandbox']});

Это работает для меня, но мне интересно, каковы минусы его реализации.

Jaygee 22.05.2024 03:32

Привет! Это работает и для меня. Но какие могут быть минусы? Есть ли другая реализация?

Piyush Aggarwal 28.05.2024 10:56

Проблема с песочницей заключается в том, что она отключает некоторые функции безопасности в Chrome, и это может открыть совершенно другую банку червей. Мой код не предназначался для производства, поэтому на тот момент я не беспокоился об этом, но с моей стороны это все еще открытый вопрос.

thedomguy 28.06.2024 13:09

У меня тоже эта проблема. Просто через неделю внезапно перестал работать

Обновлено: Как упоминалось выше, отключение песочницы — это обходной путь, но скорее хак. Проблема заключается в разрешениях. Если вы запустите page.pdf({ dumpio: 'true' }), вы увидите вывод ошибок.

ИСПРАВИТЬ:

https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#chrome-reports-sandbox-errors-on-windows

Вы можете попытаться решить эту проблему, вручную установив необходимые разрешения для исполняемого файла Chromium с помощью команды icacls, как предложено в сообщении об ошибке. Откройте командную строку от имени администратора и выполните следующую команду:

icacls %USERPROFILE%/.cache/puppeteer/chrome /grant *S-1-15-2-1:(OI)(CI)(RX)

Эта команда предоставляет необходимые разрешения исполняемому файлу Chromium, который использует Puppeteer.

Другие вопросы по теме