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/LICENSE

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. 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.
  2. 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.
  3. If your tool writes to stdout instead of a file, wrap it (sh -c 'tool in > out') so the placeholder receives the bytes.
  4. Ensure no tmp cleaner daemon is removing files from the system temp dir mid-request; point TMPDIR to a stable writable directory if needed.
  5. 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

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


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/3bd7f4c495c0a164. Report an issue: GitHub.