Skip to content

Troubleshooting

The bridge raises three exception types. The exception you catch tells you what failed and whether to retry or use a fallback. Each message fragment below comes from the source.

ExceptionExtendsMeaning
CloudflareNotAvailableExceptionNextPDF\Exception\NextPdfExceptionThe bridge cannot reach the edge, or config is incomplete, and no usable fallback exists.
CloudflareRenderExceptionNextPDF\Exception\NextPdfExceptionThe Worker answered, but the render failed (Hypertext Transfer Protocol (HTTP) error or malformed body). Never falls back.
InvalidSpkiPinExceptionInvalidArgumentExceptionA configured Subject Public Key Info (SPKI) pin string is malformed.

CloudflareSecurityPolicy also raises RuntimeException directly for input and Uniform Resource Locator (URL) policy violations. It raises the exception before it sends any request.

Message fragmentRaised byCauseFix
incomplete (missing worker_url or api_token)Renderer (via fallback path)Either workerUrl or apiToken is emptySet both, then verify with isValid().
HTML input exceeds maximum sizeCloudflareSecurityPolicy::validate()The Hypertext Markup Language (HTML) input is longer than maxHtmlSizeReduce the input, or raise maxHtmlSize deliberately.
Base64 data URI exceeds safety limitCloudflareSecurityPolicy::validate()A data:;base64, Uniform Resource Identifier (URI) is estimated above 13631488 bytesExternalize the asset; do not inline large binaries.
meta-refresh redirect which could cause SSRFCloudflareSecurityPolicy::validate()A <meta http-equiv="refresh"> tag could trigger server-side request forgery (SSRF)Remove the tag; use a server-side redirect outside the rendered HTML.
Invalid Worker URLvalidateWorkerUrl()The URL does not parse or lacks scheme/hostProvide a full absolute URL that uses Hypertext Transfer Protocol Secure (HTTPS).
Worker URL must use HTTPSvalidateWorkerUrl()Scheme is not HTTPSUse https://.
private or reserved IP addressesvalidateWorkerUrl()Internet Protocol (IP) literal in Request for Comments (RFC) 1918 / loopback / RFC 3927 rangePoint at a public endpoint.
hostname resolves to a private or reserved IPvalidateWorkerUrl()A resolved Domain Name System (DNS) A/AAAA record is private or reservedFix DNS; investigate possible rebinding.
DNS answer changed since validationassertPinsStillValid()The host resolved to a new IP address between check and sendRe-resolve; treat as a possible rebinding attempt.

These are CloudflareRenderException failures. The Worker answered, but the render itself failed. These never trigger the local fallback because the edge was reachable.

Message fragmentCause
Cloudflare Worker returned HTTP <code>: <detail>Non-200 status. Detail comes from the JavaScript Object Notation (JSON) error field or the first 200 body bytes.
Worker returned empty or invalid PDF dataBinary response does not start with %PDF.
Worker error: <message>JSON response that carries an error field.
JSON response missing "pdf" fieldJSON response without a pdf field.
Invalid base64-encoded PDF in JSON responseThe pdf field did not base64-decode to bytes starting with %PDF.
Invalid JSON response from WorkerBody uses Content-Type: application/json, but does not decode to an array.
Unexpected Content-Type from Worker: <type>A 200 response whose Content-Type is neither application/pdf nor application/json.

When you catch one of these, inspect the Worker logs. The failure is on the Worker side, not in this bridge.

These are CloudflareNotAvailableException failures. The bridge could not use the edge, and no fallback produced a Portable Document Format (PDF) file.

Message fragmentCauseFix
Cloudflare Worker unavailable: <reason>Transport error with fallback disabledEnable fallbackToLocal and wire a factory, or fix connectivity.
Artisan is installed but no LocalRendererFactoryInterface was providednextpdf/artisan is present, but no factory was passedPass a LocalRendererFactoryInterface to the constructor.
local Chrome fallback (nextpdf/artisan) is not installedFallback is enabled, no factory is configured, and Artisan is absentRun composer require nextpdf/artisan, then wire a factory.

When you supply a PHP Standards Recommendation (PSR)-3 logger and the fallback path runs, the bridge logs a warning (Cloudflare render failed, attempting fallback), then an info (Falling back to local renderer).

SymptomCauseFix
InvalidSpkiPinException: Invalid SPKI pin formatA pin is not in sha256/<base64> (or sha256//<base64>) formCorrect the pin string.
cURL transport error (<n>): <msg>cURL-level failure (Transport Layer Security (TLS), DNS, timeout)Inspect the cURL error number; if pins are set, confirm the served SPKI is still pinned.
Renders fail immediately after certificate rotationThe new certificate’s SPKI is not in the pin setAdd the new SPKI as a backup pin before rotating.
Pinned transport not used despite pins configuredNo PSR-17 ResponseFactory was suppliedPass a ResponseFactory; the pinned transport requires it.

isAvailable() never throws. It returns false when configuration is invalid or when the HEAD probe fails or raises an exception. It returns true only when the probe responds with a status below 500. A true result is only a hint: the subsequent POST can still fail with any of the Worker-side errors above. Do not treat a passing probe as a guarantee.

ApiProtection keeps limits in memory per process. Counts do not survive a restart, and they are not shared across workers or nodes. If one node allows a client and another denies it, that is expected. Put a shared store in front of the limiter for a cluster-wide limit.

  • /integrations/cloudflare/security-and-operations/ — the operational runbook and controls behind these messages.
  • /integrations/cloudflare/quickstart/ — the canonical try/catch pattern.
  • /integrations/cloudflare/production-usage/ — fallback wiring details.