Skip to main content

render_timeout

HTTP 504 · code: "render_timeout" · type: https://docs.formfeed.dev/errors/render-timeout

A step of the render took longer than its limit. There is no limit on the render as a whole; each step has its own:

StepLimitdetail
The template language (Jinja2, Handlebars) fills the template with the data5 secondsTemplate render took 6120 ms; the limit is 5000 ms
Chromium loads the document, until none of its requests is running (waitFor.networkIdle says how)30 secondsRender exceeded 30000 ms
Waiting until an element matching waitFor.selector is visiblewaitFor.timeoutMs, 5 seconds by default, at most 30Render exceeded 30000 ms (the message names 30 seconds in any case)
Taking the picture of an image output, or of the element of image.clipSelector30 secondsRender exceeded 30000 ms
Printing the PDF120 secondsPrinting the PDF exceeded 120000 ms
Filling a Word or PowerPoint template10 secondsTemplate render took … ms; the limit is 10000 ms
Converting a Word or PowerPoint template, or a document sent to /pdf/convert, to PDF60 seconds for the whole render or conversionThe conversion did not finish in time
Making an e-invoice, or validating a PDF/UA-1 file20 secondsThe e-invoice conversion did not finish in time, The PDF/UA validation did not finish in time

A document of several hundred pages prints in tens of seconds, well inside its limit. A waitFor.delayMs pause has no limit of its own and is capped at 10 seconds. A Liquid template that runs longer than 5 seconds answers template_runtime_error instead, and a url render whose page does not load in 30 seconds answers navigation_failed.

Handlebars partials nested deeper than 8 levels also answer this code (Partials nest deeper than 8), although no time ran out. Jinja2 and Liquid have no such depth limit.

The render is recorded as failed and costs nothing. How long a render may take lists the same limits from the request's side.

Example​

{
"type": "https://docs.formfeed.dev/errors/render-timeout",
"title": "Render timed out",
"status": 504,
"detail": "Render exceeded 30000 ms",
"code": "render_timeout",
"location": null,
"render_id": "rnd_7da09ab6cd9946b798c187c2a9350852",
"request_id": "req_4b7c1e9a2f3d4c5b8a6e7f9012"
}

Common causes​

  • A template that loops over very large data, or nests loops deeply.
  • Images, fonts or scripts from a slow server: the document counts as loaded only when none of its requests is running.
  • A script that keeps the network busy, such as polling or an analytics beacon.
  • A waitFor.selector that never becomes visible, because the script that adds the element failed, the selector does not match, or the element stays hidden.
  • A Word or PowerPoint file with many large pictures or pages, which LibreOffice converts slowly.

How to fix it​

  • Reduce the work in the template: compute totals and groupings in your application and send the results.
  • Host images and fonts on a fast CDN, or in the file library, and check that waitFor.selector matches an element your script really adds.
  • Set waitFor.networkIdle to false for documents that keep a connection open.
  • For office documents, compress the pictures, or split the document.

Retry?​

Only if the cause was passing, such as a slow server for images. A template that is too slow fails the same way again. An answer of 504 is not stored under the Idempotency-Key, so a retry with the same key really runs. Async renders are retried by the queue twice, and a render that times out every time ends as worker_exhausted; a timeout of a Word or PowerPoint conversion is not retried.