Artisan troubleshooting
At a glance
Section titled “At a glance”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.
Diagnose by exception
Section titled “Diagnose by exception”ChromeNotAvailableException
Section titled “ChromeNotAvailableException”
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. Setchrome_binaryto 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: trueafter 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.
ChromeRenderException — empty output
Section titled “ChromeRenderException — empty output”
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 contains | Cause | Fix |
|---|---|---|
exceeds maximum allowed size | HTML exceeds maxHtmlSize | Reduce the input or raise max_html_size (widens the resource-exhaustion surface) |
oversized base64 data URI | Inline base64 data Uniform Resource Identifier (URI) ≥ 13 MB | Shrink the embedded asset; reference a smaller image |
forbidden meta refresh redirect | <meta http-equiv="refresh"> present | Remove 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.
PdfParseException
Section titled “PdfParseException”
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.
Remote assets render blank
Section titled “Remote assets render blank”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/.
Content clipped at the bottom
Section titled “Content clipped at the bottom”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/.
Latency spike every ~100 renders
Section titled “Latency spike every ~100 renders”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.
Memory grows over a long batch
Section titled “Memory grows over a long batch”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.
Diagnostic checklist
Section titled “Diagnostic checklist”- Reproduce with a minimal trusted HTML fragment to separate input issues from environment issues.
- Run
chromium --headless --dump-dom about:blankon the host as the worker user. - Inject a PHP Standards Recommendation 3 (PSR-3) logger and read the
info/noticelines, including the binary path and restart count. - Confirm
chrome-php/chromeis installed:BrowserPool::getBrowser()will not throwChromeNotAvailableExceptionwhen it is available. - Check the exception’s previous exception for the underlying Chrome cause.
See also
Section titled “See also”- /integrations/artisan/install/
- /integrations/artisan/configuration/
- /integrations/artisan/security-and-operations/
- /integrations/artisan/chrome-renderer-setup/
- /integrations/artisan/production-usage/