{"record":{"id":"ca631e62d09e8e45","repo":"BookStackApp/BookStack","slug":"pdf-export-via-command-failed-with-exit-code-pro","errorCode":null,"errorMessage":"PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}","messagePattern":"PDF Export via command failed with exit code (.+?), stdout: (.+?), stderr: (.+?)","errorType":"exception","errorClass":"PdfExportException","httpStatus":null,"severity":"error","filePath":"app/Exports/PdfGenerator.php","lineNumber":165,"sourceCode":"\n        $cleanup = function () use ($inputHtml, $outputPdf) {\n            foreach ([$inputHtml, $outputPdf] as $file) {\n                if (file_exists($file)) {\n                    unlink($file);\n                }\n            }\n        };\n\n        try {\n            $process->run();\n        } catch (ProcessTimedOutException $e) {\n            $cleanup();\n            throw new PdfExportException(\"PDF Export via command failed due to timeout at {$timeout} second(s)\");\n        }\n\n        if (!$process->isSuccessful()) {\n            $cleanup();\n            throw new PdfExportException(\"PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}\");\n        }\n\n        $pdfContents = file_get_contents($outputPdf);\n        $cleanup();\n\n        if ($pdfContents === false) {\n            throw new PdfExportException(\"PDF Export via command failed, unable to read PDF output file\");\n        } else if (empty($pdfContents)) {\n            throw new PdfExportException(\"PDF Export via command failed, PDF output file is empty\");\n        }\n\n        return $pdfContents;\n    }\n\n    protected function renderUsingWkhtml(string $html): string\n    {\n        $snappy = new SnappyPdf($this->getWkhtmlBinaryPath());\n        $options = config('exports.snappy.options');","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Exports/PdfGenerator.php#L147-L183","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the stdout/stderr embedded in the exception message — it contains the external tool's actual error.","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}.","Run the exact command manually on a temp HTML file to reproduce and fix the underlying renderer failure.","Install missing runtime dependencies of the renderer in the container (fonts, libX11*, fontconfig, etc.).","If a recent deployment changed the command config, diff it against the vendor/docs default and restore known-good syntax."],"exampleFix":"// before (.env)\nPDF_COMMAND=/usr/bin/wkhtmltopdf {input_html_path} {output_pdf_path}  # binary absent in slim image\n\n// after\n# install it, or point to the real path:\nPDF_COMMAND=/usr/local/bin/wkhtmltopdf --quiet {input_html_path} {output_pdf_path}","handlingStrategy":"try-catch","validationCode":"<?php\n// verify the configured binary and placeholders before relying on command-mode export\n$cmd = config('exports.pdf_command');\n$hasPlaceholders = str_contains($cmd, '{input_html_path}') && str_contains($cmd, '{output_pdf_path}');\n$binary = trim(strtok($cmd, ' '));\n$isReady = $hasPlaceholders && shell_exec('command -v ' . escapeshellarg($binary)) !== null;","typeGuard":null,"tryCatchPattern":"try {\n    $pdf = $pdfGenerator->fromHtml($html);\n} catch (\\BookStack\\Exceptions\\PdfExportException $e) {\n    if (str_contains($e->getMessage(), 'failed with exit code')) {\n        // the message already embeds stdout/stderr; log it verbatim for diagnosis\n        Log::error('PDF command failed', ['detail' => $e->getMessage()]);\n    }\n    throw $e;\n}","preventionTips":["Pin and verify the renderer binary exists in every environment (Dockerfile HEALTHCHECK or composer script).","Keep exports.pdf_command in version control and review it on renderer upgrades.","Install all renderer runtime dependencies (fonts, X libs) in minimal container images.","Capture and monitor the stderr text embedded in the exception to catch regressions early."],"tags":["php","process-exit-code","symfony-process","pdf-export","shell-command"],"backgroundTag":"process-exited-nonzero","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}