Fix wkhtmltopdf failures in Laravel by isolating the failing layer in this order: run the exact binary from a macOS shell, point Snappy at that same executable, verify permissions and CPU architecture, repair Homebrew and runtime dependencies, then test fonts, URLs, JavaScript and local-file access. An exit status 126 is usually an executable or architecture problem, not a Blade or Laravel problem.
Start with the failure layer, not Laravel code
Laravel calls Snappy, and Snappy launches wkhtmltopdf as a separate process. A failure can therefore occur before Laravel renders a view, while the binary starts, while it loads libraries and fonts, or while it processes your HTML. The first useful question is whether the same executable can create a PDF outside PHP.
| Symptom | Likely layer | First check |
|---|---|---|
| Shell says “cannot execute binary file” or Laravel reports exit status 126 | Permission, wrong file type or CPU architecture | ls -l, file, uname -m |
| Terminal works, Laravel fails | Snappy points to another path or has a different environment | Print the configured binary path and run that exact path manually |
| “No such file or directory” for an apparently present binary | Bad path or missing runtime loader/library | Confirm the path and inspect the file type and stderr |
| PDF is blank, missing images or has wrong fonts | URL reachability, fontconfig/freetype or page timing | Reduce the HTML to a tiny fixture and test assets separately |
| Local CSS/images are denied | wkhtmltopdf local-file restrictions | Use controlled URLs or a narrowly justified local-file option |
1. Capture the exact error before changing anything
Save the complete Laravel exception and stderr, not just the final line. Record:
- The complete command path Snappy attempted to run.
- The full stderr output and numeric exit status.
- PHP, Laravel and Snappy versions.
- Your macOS version and whether the Mac is Intel or Apple Silicon.
- A minimal HTML/CSS/JavaScript example that reproduces the failure.
The wkhtmltopdf project asks for the binary version, operating-system version, detailed description and a small test case when reporting an issue. Keeping this information also lets you distinguish a renderer defect from configuration, Homebrew or application markup.
#1 Best Overall
2. Prove the binary works outside Laravel
Laravel Snappy’s README says that after installation you should be able to run wkhtmltopdf from the command line or shell. Do that with the exact file configured for your application.
- Locate the executable you intend to use. Use the path from your Snappy configuration, or inspect candidates with
which wkhtmltopdfand your package manager. - Ask it for its version and preserve both stdout and stderr:
WKHTMLTOPDF='/path/to/wkhtmltopdf'; "$WKHTMLTOPDF" --version 2>&1 - Create a tiny fixture that does not depend on Laravel:
printf '<!doctype html><html><body><h1>wkhtmltopdf test</h1></body></html>' > /tmp/wk-test.html - Convert it directly:
"$WKHTMLTOPDF" /tmp/wk-test.html /tmp/wk-test.pdf 2>/tmp/wkhtmltopdf.stderr; status=$?; cat /tmp/wkhtmltopdf.stderr; echo "exit=$status"; ls -l /tmp/wk-test.pdf
A successful shell conversion moves the investigation to Laravel’s path, environment or options. If this test fails, changing a Blade view will not repair the renderer; fix the executable or its dependencies first.
3. Point Snappy at the real macOS executable
Publish Snappy’s configuration and set binary to a file that actually exists on this Mac. Composer-installed binaries and system or Homebrew binaries are different installations; a Linux path copied from a deployment guide will not work on macOS.
- Publish the package configuration using the vendor-publish command documented by the Snappy package you installed. This creates
config/snappy.php. - Open that file and set the binary value to the absolute macOS path. For example:
'binary' => env('WKHTMLTOPDF_BINARY', '/opt/homebrew/bin/wkhtmltopdf'), - Put the same path in
.envwhen you want machine-specific configuration:WKHTMLTOPDF_BINARY=/opt/homebrew/bin/wkhtmltopdf - Clear cached Laravel configuration, then verify the resolved value. A stale config cache can make a corrected file appear to have no effect:
php artisan config:clear - Run the exact resolved path from step 2 again. Do not compare a successful interactive
which wkhtmltopdfresult with a different path inconfig/snappy.php.
In application code, keep PDF generation simple while diagnosing the executable:
Recommended Free Tools
<?php
use BarryvdhSnappyFacadesPdf;
$pdf = Pdf::loadView('invoice', ['invoice' => $invoice]);
return $pdf->download('invoice.pdf');
Remove custom options temporarily. Once a plain view converts, add options back one at a time so the first failing option is visible.
4. Fix exit status 126, permissions and CPU architecture
Status 126 means the shell found a file but could not execute it. Treat it as an executable or architecture problem before investigating HTML.
Check permissions
Inspect the mode and ownership:
ls -l /path/to/wkhtmltopdf
The file needs an execute bit for the user running PHP. If you control the file and it is otherwise trustworthy, add execute permission with chmod +x /path/to/wkhtmltopdf. Do not blindly change permissions on a package-managed location; correct the package installation or ownership instead.
Check that it is a macOS binary
file /path/to/wkhtmltopdf
uname -m
An Apple Silicon Mac normally reports arm64; an Intel Mac reports x86_64. Do not run a Linux amd64 executable on macOS. A documented M1 failure occurred because an x86_64 binary could not execute in that environment. Use a macOS build matching the host architecture, or deliberately provide the translation environment required by the particular build and verify it from the shell before involving Laravel.
Check for a path that is a script or broken link
ls -l "$(which wkhtmltopdf)"
file "$(which wkhtmltopdf)"
A symlink may lead to an uninstalled file, and a path copied from another machine may point into a nonexistent Composer directory. Resolve the link and configure the existing target.
5. Repair Homebrew and mixed Intel/Apple Silicon installations
Homebrew’s normal prefix is /opt/homebrew on Apple Silicon and /usr/local on Intel. A Mac upgraded over time can contain both trees, leaving your interactive shell, PHP process and Snappy with different tools.
Rank #3
- Check which Homebrew and renderer are being used:
which brew
brew --prefix
which wkhtmltopdf
brew --prefix wkhtmltopdf - Compare the result with your Mac architecture:
uname -m - Refresh package metadata and inspect diagnostics:
brew update
brew doctor - Read every warning from
brew doctor, especially stale Command Line Tools after a macOS upgrade and PATH entries that mix/usr/localwith/opt/homebrew. - Retry the original direct conversion while preserving complete output.
Do not assume that fixing your shell’s PATH fixes PHP-FPM, a queue worker or a web-server process. An absolute binary path in Snappy is more reproducible than relying on an inherited PATH.
6. Check libraries, fonts and rendering inputs
wkhtmltopdf builds depend on platform runtime libraries and on fontconfig and freetype configuration. Laravel Snappy documentation also notes that dependencies such as libXrender may require manual installation, depending on the build. Use the dependency instructions for the exact macOS package you installed; do not copy Linux package commands into Homebrew.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Use a staged rendering test
- Convert plain text and a system font with no CSS or images.
- Add a local stylesheet.
- Add one image using an absolute, reachable URL.
- Add web fonts only after ordinary text renders correctly.
- Finally add JavaScript and remote assets.
This identifies whether the failure is process startup, font discovery, a blocked URL or page timing. Confirm fonts are installed for the account that runs the PHP process; a font visible in your desktop session may not be visible to a queue worker.
The stable wkhtmltopdf series identified by the project is 0.12.6, released June 11, 2020. Treat the version printed by your installed executable as authoritative, because package provenance and build options affect available behavior.
7. Handle local files without weakening security
Modern wkhtmltopdf behavior can block local-file access. Prefer serving CSS, images and other assets through controlled URLs that the renderer can reach. If a trusted, sanitized document genuinely requires local files, enable access narrowly and test the smallest directory needed.
Rank #4
KnpLabs’ Snappy README warns that “The --enable-local-file-access option in wkhtmltopdf can be risky if used with untrusted HTML or JavaScript.” The wkhtmltopdf project likewise warns not to use it with untrusted HTML. Never pass user-supplied markup, JavaScript or arbitrary file paths to a process that can read local files. Keep authorization, path validation and input sanitization outside the renderer.
8. Make Laravel’s execution environment match your shell
When Terminal succeeds but a controller, queue or scheduler fails, compare environments rather than reinstalling Laravel.
- Log the absolute Snappy binary path at runtime (without exposing secrets).
- Run the same PHP user and working directory used by the failing process where practical.
- Confirm that the process can read the Blade view, temporary directory, fonts and any asset URLs.
- Use an absolute binary path instead of assuming the web server inherited your interactive shell PATH.
- Clear Laravel’s configuration cache after changing
config/snappy.phpor.env.
For queue workers, restart the worker after configuration changes. A long-running worker can retain old configuration even after the files are corrected.
9. Diagnose HTML, CSS and JavaScript after startup works
Once the tiny shell fixture succeeds, reduce your application document until it succeeds too. Remove scripts, remote stylesheets, web fonts and large images, then restore each category separately. Check that every URL is reachable from the Mac, that redirects do not require an interactive login, and that generated asset URLs use the correct scheme and host.
If content appears only after JavaScript runs, use the Snappy option that waits for the required condition, such as a selector or a deliberate delay, and keep the wait no longer than necessary. A renderer timeout can otherwise look like a Laravel failure. Capture stderr for warnings about blocked resources, TLS or malformed markup.
Free tools Windows power users keep installed
One-click scans. No signup required.
Best Value
10. Build a reproducible local and deployment setup
Document the binary provenance, version, architecture, absolute path, required libraries and fonts, and the exact command that converts your minimal fixture. Keep that fixture in the project’s diagnostic notes or test suite. On each development or deployment machine, verify:
uname -mmatches the renderer build you selected.- The configured absolute path exists and is executable.
- The binary prints its version without an error.
- The minimal HTML conversion creates a non-empty PDF.
- The PHP process can read required templates and assets.
This prevents a Composer update, Homebrew migration or macOS upgrade from silently switching the executable underneath Laravel.
Common errors and precise fixes
| Error or symptom | Cause to test | Fix |
|---|---|---|
| Exit status 126 | Non-executable file, wrong binary format or architecture mismatch | Run ls -l, file and uname -m; install a macOS build and correct execute permission |
| Exit status 127 or “command not found” | Snappy path is wrong or target was removed | Find the existing executable and set its absolute path in config/snappy.php; clear config cache |
| Terminal succeeds, Laravel fails | Different path, PATH, user, permissions or working directory | Run the exact configured path as the PHP process and log the resolved configuration |
| Missing library or loader message | Incomplete package or stale developer tools | Run brew update and brew doctor; follow the build’s dependency instructions |
| Blank PDF or missing glyphs | Fontconfig/freetype, unavailable fonts or renderer timing | Test a plain document, install/verify fonts for the service user, then add assets incrementally |
| Images or CSS missing | Unreachable URL, blocked local file or authentication requirement | Use controlled reachable URLs, validate permissions and avoid broad local-file access |
| Works on Intel but not M1/M2 | x86_64 versus arm64 binary or mixed Homebrew prefixes | Match the build to the host, inspect both Homebrew prefixes and remove ambiguous PATH entries |
11. Escalate with a minimal reproduction
If the direct fixture still fails after permissions, architecture, Homebrew and dependencies are correct, submit a focused report. Include the binary’s --version output, macOS version, uname -m result, exact command, complete stderr and the smallest HTML/CSS/JavaScript file that fails. State whether the failure occurs in a shell, a web request, a queue worker or all three. This gives maintainers enough information to separate a renderer defect from Laravel configuration, package installation or application markup.
Or skip the browser setup
If your goal is a clean screenshot or PDF of a web page rather than a wkhtmltopdf-specific Laravel pipeline, ScreenshotNeo makes the capture a single API request. It accepts the consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets before the capture; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemscURL:
curl -G 'https://api.screenshotneo.com/v1/shot' -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for all options, including full-page capture, element selectors, device and viewport settings, retina scale, PDF paper and margin controls, custom CSS and JavaScript, click and wait actions, request blocking, headers, cookies, user agent, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous webhooks, bulk capture and usage reporting.
Python:
import requests
r = requests.get('https://api.screenshotneo.com/v1/shot', params={'access_key': 'YOUR_API_KEY', 'url': 'https://stripe.com'}, timeout=90)
r.raise_for_status()
open('shot.webp', 'wb').write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
The Free plan includes 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 screenshots, and every feature is included on every plan. Create a free ScreenshotNeo account.
Frequently Asked Questions
Can ScreenshotNeo reproduce a PDF generated by my Laravel view?
It captures a publicly reachable URL, not a PHP view that exists only inside your application process. Deploy or expose the page through an authenticated, controlled URL first, then use the capture options or capture_pdf tool.
Should I switch from wkhtmltopdf solely because it is old?
No. First establish whether your current 0.12.6-based build works for your document and security requirements. Replace it when you need a maintained rendering stack, a different CSS/JavaScript capability or a deployment model your current binary cannot support.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →What is the safest way to test a local-file workaround?
Use a sanitized fixture containing only files from a dedicated directory, run it under the same service account as Laravel, and remove the local-file option after confirming that controlled URLs can serve the required assets.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




