It will not run
PDF generation is too slow
A render takes several seconds. Users watch a spinner, and a batch of a few hundred documents takes long enough to need its own monitoring.
What is actually happening
Usually waiting rather than working. The default wait condition holds until the network is quiet, so every third-party request on the page is added to your render time. After that, the largest cost is starting a browser, which is most of the time when you start one per request.
Confirming it is this and not something that looks like it
Time the render with all external references stripped from the HTML. The difference between that and your current number is the waiting, and it is usually most of it.
The fix
Remove external requests from the document, inline the CSS and the fonts, and reuse the render path rather than starting something new per document. For batches, send the batch rather than looping: the overhead is paid once instead of per document.
// A loop pays the per-request overhead once per document.
for (const payslip of payslips) {
await renderOne(payslip); // 400 documents, 400 round trips
}
// The batch endpoint pays it once.
const result = await fetch("https://api.pdfpipe.xyz/v1/pdf/batch", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PDFPIPE_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
documents: payslips.map((p) => ({ html: renderPayslip(p), name: `${p.id}.pdf` })),
options: { format: "A4", printBackground: true },
}),
});If that was not it
These produce the same symptom often enough to be worth ruling out before assuming the fix above did not work.
- A cold start on every request, where a warm path would be a fraction of it
- Images fetched over the network during the render rather than embedded
- Very large DOMs, where layout itself becomes the cost
- Rendering synchronously inside a user-facing request instead of queueing it
Frequently asked
Does this happen with every rendering engine?
The behaviour behind it is not specific to one tool. Usually waiting rather than working. Anything rendering HTML to a paged medium has to make the same decision, so the fix travels with you if you change how the render happens.
Will this show up as an error in my logs?
Yes, this one surfaces as a thrown error or a failed status, which is why it is at least findable. The harder half is that the message often names the call that was in flight rather than the thing that actually failed.
Related failures
Problems people arrive at from the same starting point, or mistake for this one.
PDF generation runs out of memory
Two multiplying factors.
Failed to launch the browser process
A headless browser is not a self-contained binary.
libnss3.so: cannot open shared object file
This is the previous error with the first missing library named.
Navigation timeout of 30000 ms exceeded
The default wait condition is the network going idle, and something on the page is keeping it busy: an analytics beacon, a font request to a host that is not responding, a polling request, or an ad script.
Protocol error (Page.printToPDF): Target closed
The tab crashed, and by far the most common reason is memory.
CSS for paged documents, and what actually works
Which parts of the paged media specification a browser-based render implements.
Everything that goes wrong, by category
The full list, grouped by where in the pipeline it breaks.
Paste your markup and see the rendered document, without signing up.