Skip to content

Artisan troubleshooting

Every bridge failure appears as a typed exception. Match the exception and message to the table below. Each row points to the source check that raised it, so you can fix the cause instead of the symptom.

chrome-php/chrome is not installed. Install it via: composer require chrome-php/chrome:^1.15

The Chrome DevTools Protocol (CDP) client library is not on the autoloader. BrowserPool::getBrowser() raises this before Chrome launches. Run the install command. This differs from a missing binary: the library check and the binary check run in separate stages.

ChromeRenderException — launch or timeout

Section titled “ChromeRenderException — launch or timeout”

Chrome renderer failed: <cause>

Chrome could not start, timed out, or crashed. The previous exception contains the original cause. Check these common causes:

  • Binary not found / not executable. Verify it with chromium --headless --dump-dom about:blank. Set chrome_binary to the absolute path.
  • Sandbox cannot initialize (containers). The cause mentions the sandbox or namespace. Use a sandbox-capable container when possible, or set no_sandbox: true after you read /integrations/artisan/security-and-operations/.
  • Timeout. A heavy document exceeded render_timeout. Raise the timeout for that workload, or reduce the document. On a user-facing path, weigh the denial-of-service trade-off.
  • Missing shared libraries. Chrome exits immediately. Install the Chrome dependency set for your distribution.

Chrome printToPDF returned empty data

Chrome started, but printToPDF returned zero bytes. The usual cause is input that creates no rendered box, such as an empty body or content set to display:none, or a Chrome crash during print. Confirm that the Hypertext Markup Language (HTML) input renders to a visible box. Check host memory pressure.

RuntimeException — input rejected pre-Chrome

Section titled “RuntimeException — input rejected pre-Chrome”
Message containsCauseFix
exceeds maximum allowed sizeHTML exceeds maxHtmlSizeReduce the input or raise max_html_size (widens the resource-exhaustion surface)
oversized base64 data URIInline base64 data Uniform Resource Identifier (URI) ≥ 13 MBShrink the embedded asset; reference a smaller image
forbidden meta refresh redirect<meta http-equiv="refresh"> presentRemove the tag; it is a server-side request forgery (SSRF) navigation vector and is always rejected

These errors come from ChromeSecurityPolicy::validate() and fire before Chrome is contacted, so they are fast and cheap to cover in tests.

Page <n> has no content stream

Chrome produced a Portable Document Format (PDF) file that the parser could not extract a page from. This is rare and usually points to malformed Chrome output or a Chrome version with an unexpected structure. Capture the Chrome version and the input. Verify that the binary is a supported Chrome/Chromium build.

This is not a bug. The bridge blocks every subresource fetch with Content Security Policy (CSP) default-src 'none' and a CDP setBlockedURLs('*') block. Remote <img>, stylesheets, fonts, scripts, and iframes do not load. Inline assets as data: URIs, and inline Cascading Style Sheets (CSS) through defaultCss or <style>. See the network model on /integrations/artisan/security-and-operations/.

The document overflowed to a second Chrome page, and the bridge imported only page 0. Either the auto-fit buffer was too small for unusually tall reflow, or an explicit height was too small. Set an explicit height sized to the content, or remove the explicit height to use auto-fit with its safety buffer. See height handling on /integrations/artisan/production-usage/.

This is expected. BrowserPool restarts Chrome every 100 renders to bound memory. A notice-level log line records the restart and render count. Treat it as a known periodic cost in service-level objectives (SLOs), not an incident. A restart rate higher than every 100 renders means the documents are heavier than expected.

BrowserPool bounds growth with the 100-render restart, but a very long-lived worker can still accumulate memory. Call close() between large batches to recycle Chrome early, and run the worker under a host memory limit.

  1. Reproduce with a minimal trusted HTML fragment to separate input issues from environment issues.
  2. Run chromium --headless --dump-dom about:blank on the host as the worker user.
  3. Inject a PHP Standards Recommendation 3 (PSR-3) logger and read the info/notice lines, including the binary path and restart count.
  4. Confirm chrome-php/chrome is installed: BrowserPool::getBrowser() will not throw ChromeNotAvailableException when it is available.
  5. Check the exception’s previous exception for the underlying Chrome cause.
  • /integrations/artisan/install/
  • /integrations/artisan/configuration/
  • /integrations/artisan/security-and-operations/
  • /integrations/artisan/chrome-renderer-setup/
  • /integrations/artisan/production-usage/