Skip to content

Troubleshooting the NextPDF Laravel package

Use this page to map each observable package failure to its verified source-level root cause. Each entry gives you the symptom, the cause, and the fix.

Terminal window
composer require nextpdf/laravel
php artisan vendor:publish --tag=nextpdf-config

Most reported issues fall into five groups: discovery, container resolution, signing, queue jobs, and Hypertext Transfer Protocol (HTTP) filenames. The package fails loudly by design. Optional features that are not configured return null, and unsafe input raises typed exceptions. The symptom usually points directly to the cause.

SymptomVerified causeFix
Provider is not registered after installApplication disables discovery with extra.laravel.dont-discoverRemove the package from dont-discover, or register NextPdfServiceProvider manually in bootstrap/providers.php
config('nextpdf') is emptyConfiguration was not merged because no advertised binding was resolved (deferred provider)Resolve any provides() entry, or confirm discovery with php artisan package:discover --ansi
config/nextpdf.php not created by publishPublish tag mismatchUse the exact tag: php artisan vendor:publish --tag=nextpdf-config
RuntimeException: “NextPDF requires the ext-mbstring/ext-zlib PHP extension”A required Hypertext Preprocessor (PHP) extension is missing at runtimeInstall or enable mbstring and zlib in php.ini
SymptomVerified causeFix
app(SignerInterface::class) returns nullSigning is disabled, or the certificate is empty in nextpdf.signatureSet signature.enabled = true and a valid signature.certificate; install nextpdf/premium to provide the signer concrete
app(TsaClient::class) returns nullnextpdf.tsa.url is emptyConfigure tsa.url (and credentials/pins as needed)
Class-not-found for a PDF/A version typenextpdf.pdfa is non-null but nextpdf/premium is not installedInstall nextpdf/premium, or set pdfa back to null
Class-not-found resolving an e-invoice contractBindings are registered, but the Premium concretes are absentInstall nextpdf/premium; e-invoice contracts resolve lazily and fail only on the first resolve without Premium
Same document mutated across two logical operationsThe document binding is a factory; you reused one resolved instanceResolve a fresh PdfDocumentInterface per document

A container without an entry throws a not-found exception on get() (PHP Standard Recommendation 11 (PSR-11) §1.1.2). The e-invoice contracts are bound, so the container’s has() returns true. The missing Premium concrete raises the error at construction time, not the container itself.

SymptomVerified causeFix
InvalidArgumentException: Path traversal sequences are not allowedThe output path contains a .. segmentUse an absolute, traversal-free path under your storage directory
InvalidArgumentException: Stream wrappers are not allowedThe path uses a scheme like php://Use a plain filesystem path
InvalidArgumentException: Output path contains null bytesThe path contains a \0 byteSanitize the path before dispatch
InvalidArgumentException: Output path must end with .pdf extensionThe path does not end in .pdf (case-insensitive)Use a .pdf (or .PDF) suffix
Job runs but file is empty or wrongThe builder closure did not return the configured documentReturn the document from the builder; the job saves the returned value
Job uses the wrong queue or timeoutnextpdf.queue.* is not set as expectedSet queue.queue, queue.connection, and queue.timeout; tries and backoff require subclassing

Path checks run inside handle() on the worker, so a bad path fails during execution, not at dispatch. This is intentional: the worker validates the serialized queue payload where it consumes it.

SymptomVerified causeFix
Download filename is document.pdf unexpectedlyYou passed an empty filename; the factory uses the defaultPass a non-empty filename
Filename lost its path or special charactersThe filename sanitizer strips path separators, control characters, and null bytesPass only the base filename; this hardening is expected
Non-ASCII filename shows mojibake in some clientsThe response emits Request for Comments 5987 (RFC 5987) filename*= for non-ASCII names; old clients read the ASCII fallbackExpected; provide an ASCII-safe name if a legacy client must match exactly
Streamed response has no Content-LengthStreamed responses omit Content-Length by design (chunked output)Expected; use the non-streamed inline()/download() if a length header is required
Terminal window
# Confirm the provider is discovered
php artisan package:discover --ansi
# Inspect merged configuration
php artisan tinker --execute="dump(config('nextpdf.queue'));"
resource: src/Laravel/NextPdfServiceProvider.php (null-check pattern)
<?php
declare(strict_types=1);
use NextPDF\Contracts\SignerInterface;
$signer = app(SignerInterface::class);
if ($signer === null) {
// Signing not configured, or nextpdf/premium not installed.
// Continue without a signature, or fail with a clear message.
}
  • With the deferred provider, a fresh install can look “broken” until the first relevant resolve. Treat package:discover listing the package as the success signal.
  • When image_cache_mb = null, the package falls back to 50 MB; only 0 disables the cache. A “cache not disabling” report usually used null.
  • When signature.level = null, the package silently falls back to PDF Advanced Electronic Signatures (PAdES) B-B. An “unexpected B-B” report usually left the level unset.

If the first requests on a long-lived worker are slow, the font registry is parsing on demand. Populate nextpdf.preload_fonts so warmup runs once at worker boot. See /integrations/laravel/configuration/ and /integrations/laravel/boot-and-discovery/ for details.

The path and filename rejections are security controls, not bugs. Do not work around them by pre-decoding or relaxing the checks. Route file output through a controlled storage path instead. See /integrations/laravel/security-and-operations/.

ClaimSourceClausereference_id
Missing container entry throws not-found on get()PSR-11 Container§1.1.2
  • /integrations/laravel/install/ — discovery and publish steps
  • /integrations/laravel/configuration/ — every key and its default
  • /integrations/laravel/production-usage/ — dependency injection (DI) and queue patterns
  • /integrations/laravel/security-and-operations/ — why the path checks exist