BookStackApp/BookStack · error · PdfExportException
PDF Export via command failed, unable to read PDF output fil
Error message
PDF Export via command failed, unable to read PDF output file
What it means
Thrown by renderUsingCommand when file_get_contents() on the temporary output PDF path returns false after the external command reported success. The library cannot read back the PDF it expects the command to have written to {output_pdf_path}, so the export fails. Usually the command exited 0 without actually writing the output file (or wrote it elsewhere / deleted it).
Source
Thrown at app/Exports/PdfGenerator.php:172
};
try {
$process->run();
} catch (ProcessTimedOutException $e) {
$cleanup();
throw new PdfExportException("PDF Export via command failed due to timeout at {$timeout} second(s)");
}
if (!$process->isSuccessful()) {
$cleanup();
throw new PdfExportException("PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}");
}
$pdfContents = file_get_contents($outputPdf);
$cleanup();
if ($pdfContents === false) {
throw new PdfExportException("PDF Export via command failed, unable to read PDF output file");
} else if (empty($pdfContents)) {
throw new PdfExportException("PDF Export via command failed, PDF output file is empty");
}
return $pdfContents;
}
protected function renderUsingWkhtml(string $html): string
{
$snappy = new SnappyPdf($this->getWkhtmlBinaryPath());
$options = config('exports.snappy.options');
return $snappy->getOutputFromHtml($html, $options);
}
/**
* Taken from https://github.com/barryvdh/laravel-dompdf/blob/v2.1.1/src/PDF.php
* Copyright (c) 2021 barryvdh, MIT License
* https://github.com/barryvdh/laravel-dompdf/blob/v2.1.1/LICENSEView on GitHub (pinned to 18f8469a1c)
Solutions
- Check that exports.pdf_command actually writes its result to the {output_pdf_path} argument and that argument order is correct for the chosen tool.
- Test the command manually: create two temp files, run the command with their paths substituted, and confirm the output file exists and is non-empty.
- If your tool writes to stdout instead of a file, wrap it (sh -c 'tool in > out') so the placeholder receives the bytes.
- Ensure no tmp cleaner daemon is removing files from the system temp dir mid-request; point TMPDIR to a stable writable directory if needed.
- If the command is a custom wrapper, make it propagate child exit codes (exit $?) so failures are caught as exit-code errors instead of silently succeeding.
Example fix
// before (.env) — tool prints PDF to stdout, never fills {output_pdf_path}
PDF_COMMAND=/usr/bin/pandoc {input_html_path} -o pdf
// after
PDF_COMMAND=/bin/sh -c 'pandoc "$0" -o "$1"' {input_html_path} {output_pdf_path} Defensive patterns
Strategy: validation
Validate before calling
<?php
// verify the command actually produces a non-empty output file before production use
$in = tempnam(sys_get_temp_dir(), 'chk-in');
$out = tempnam(sys_get_temp_dir(), 'chk-out');
file_put_contents($in, '<h1>test</h1>');
$cmd = str_replace(
['{input_html_path}', '{output_pdf_path}'],
[escapeshellarg($in), escapeshellarg($out)],
config('exports.pdf_command')
);
shell_exec($cmd);
$isReady = file_exists($out) && filesize($out) > 0;
@unlink($in); @unlink($out); Try / catch
try {
$pdf = $pdfGenerator->fromHtml($html);
} catch (\BookStack\Exceptions\PdfExportException $e) {
if (str_contains($e->getMessage(), 'unable to read PDF output file')) {
// command exited 0 but wrote nothing; audit exports.pdf_command placeholder usage
Log::error('PDF output missing despite success exit code', ['cmd' => config('exports.pdf_command')]);
}
throw $e;
} Prevention
- Ensure the configured command's output destination is exactly {output_pdf_path}, in the correct argument position.
- Wrap stdout-producing tools with a shell redirect so bytes land in the placeholder file.
- In wrapper scripts, propagate the child exit code so silent failures surface as exit-code errors.
- Run a one-shot end-to-end command smoke test on deploy to confirm a non-empty PDF is produced.
When it happens
Trigger: Calling fromHtml() with exports.pdf_command configured; the command exits 0 but never writes to the {output_pdf_path} temp file — e.g. the command ignores the placeholder, writes to a hard-coded path, is a wrapper script that swallows failures, or the temp file was removed by an external cleaner between write and read.
Common situations: Misconfigured pdf_command whose argument order means {output_pdf_path} is not the output destination (e.g. a tool where flags come after the input); a command that prints the PDF to stdout instead of writing the file; tmpwatch/systemd-tmpfiles purging sys_get_temp_dir(); custom scripts exiting 0 regardless of the child's result.
Related errors
- PDF Export via command failed with exit code {$process->getE
- PDF Export via command failed due to timeout at {$timeout} s
- Failed to load key from file path with error: {$exception->g
- Failed to create required font data in {$expectedPath}, Ensu
- Failed to create required font data at $expectedUfm, Ensure
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/3bd7f4c495c0a164.
Report an issue: GitHub.