BookStackApp/BookStack · error · PdfExportException

PDF Export via command failed with exit code {$process->getE

Error message

PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}

What it means

Thrown by renderUsingCommand when the configured external PDF command finishes with a non-zero exit code. The exception message embeds the exit code plus the command's stdout and stderr, so the actual renderer error text is included. It means the external tool ran but failed to produce the PDF.

Source

Thrown at app/Exports/PdfGenerator.php:165

        $cleanup = function () use ($inputHtml, $outputPdf) {
            foreach ([$inputHtml, $outputPdf] as $file) {
                if (file_exists($file)) {
                    unlink($file);
                }
            }
        };

        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');

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Read the stdout/stderr embedded in the exception message — it contains the external tool's actual error.
  2. Verify the command binary exists in PATH at runtime (which wkhtmltopdf / docker exec which ...) and that exports.pdf_command uses the correct placeholders {input_html_path} and {output_pdf_path}.
  3. Run the exact command manually on a temp HTML file to reproduce and fix the underlying renderer failure.
  4. Install missing runtime dependencies of the renderer in the container (fonts, libX11*, fontconfig, etc.).
  5. If a recent deployment changed the command config, diff it against the vendor/docs default and restore known-good syntax.

Example fix

// before (.env)
PDF_COMMAND=/usr/bin/wkhtmltopdf {input_html_path} {output_pdf_path}  # binary absent in slim image

// after
# install it, or point to the real path:
PDF_COMMAND=/usr/local/bin/wkhtmltopdf --quiet {input_html_path} {output_pdf_path}
Defensive patterns

Strategy: try-catch

Validate before calling

<?php
// verify the configured binary and placeholders before relying on command-mode export
$cmd = config('exports.pdf_command');
$hasPlaceholders = str_contains($cmd, '{input_html_path}') && str_contains($cmd, '{output_pdf_path}');
$binary = trim(strtok($cmd, ' '));
$isReady = $hasPlaceholders && shell_exec('command -v ' . escapeshellarg($binary)) !== null;

Try / catch

try {
    $pdf = $pdfGenerator->fromHtml($html);
} catch (\BookStack\Exceptions\PdfExportException $e) {
    if (str_contains($e->getMessage(), 'failed with exit code')) {
        // the message already embeds stdout/stderr; log it verbatim for diagnosis
        Log::error('PDF command failed', ['detail' => $e->getMessage()]);
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling fromHtml() with exports.pdf_command configured and Process::run() completing (no timeout) with $process->isSuccessful() === false — i.e. the shell command exits non-zero for any reason: missing binary, bad option, parse error on the HTML, missing dependencies.

Common situations: pdf_command referencing a binary not installed in the container (command not found, exit 127); wrong command syntax or placeholder usage after upgrading; the renderer rejecting the generated HTML/CSS; missing fonts or libraries (libX11, fontconfig) in minimal images; AppArmor/seccomp blocking the renderer.

Related errors


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