{"record":{"id":"b5ede5665fc9a786","repo":"BookStackApp/BookStack","slug":"pdf-export-via-command-failed-due-to-timeout-at","errorCode":null,"errorMessage":"PDF Export via command failed due to timeout at {$timeout} second(s)","messagePattern":"PDF Export via command failed due to timeout at (.+?) second\\(s\\)","errorType":"exception","errorClass":"PdfExportException","httpStatus":null,"severity":"error","filePath":"app/Exports/PdfGenerator.php","lineNumber":160,"sourceCode":"        file_put_contents($inputHtml, $html);\n\n        $timeout = intval(config('exports.pdf_command_timeout'));\n        $process = Process::fromShellCommandline($command);\n        $process->setTimeout($timeout);\n\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    }","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Exports/PdfGenerator.php#L142-L178","documentation":"Thrown by renderUsingCommand in app/Exports/PdfGenerator.php when the configured external PDF command (exports.pdf_command, run via Symfony Process) exceeds exports.pdf_command_timeout seconds and Symfony aborts it with ProcessTimedOutException. The library cleans up both temp files (HTML input and PDF output) and rethrows as this PdfExportException. It means the external renderer hung or was too slow for the configured limit.","triggerScenarios":"Calling fromHtml() with exports.pdf_command configured; the shell command (placeholders {input_html_path}/{output_pdf_path} substituted) runs longer than intval(config('exports.pdf_command_timeout')) seconds, e.g. the external tool waits for stdin, blocks on a network resource, or crashes into a hang.","commonSituations":"Very large/complex documents that genuinely need more time; a misconfigured command that prompts or waits for input; wkhtmltopdf/chromium hanging on remote images with no network access; pdf_command_timeout set too low (or 0/negative causing odd behavior); zombie processes from a previous crashed run holding locks.","solutions":["Increase exports.pdf_command_timeout (e.g. export PDF_COMMAND_TIMEOUT=120 in .env) to a value above the document's real render time.","Run the exact configured command manually against a sample HTML file to find why it hangs (waiting for input, remote assets, missing flags).","Make the command non-interactive: add flags like --no-sandbox / --disable-gpu / --quiet, and avoid commands that prompt on failure.","Remove or lower timeouts on remote resources referenced in the HTML (images/CSS) or ensure the server can reach them, since blocked fetches stall the renderer.","Check for stuck/zombie renderer processes from prior runs (ps aux | grep pdf/wkhtmltopdf) and kill them."],"exampleFix":"// before (.env)\nPDF_COMMAND_TIMEOUT=30  # large books exceed 30s\n\n// after\nPDF_COMMAND_TIMEOUT=300","handlingStrategy":"retry","validationCode":"<?php\n// before exporting, sanity-check the command and timeout config\n$cmd = config('exports.pdf_command');\n$timeout = intval(config('exports.pdf_command_timeout'));\n$binary = trim(strtok($cmd, ' '));\n$isReady = $cmd && $timeout > 0 && 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 due to timeout')) {\n        // retry with a bigger timeout or queue the export to a background job\n        Log::warning('PDF command timed out; consider PDF_COMMAND_TIMEOUT increase');\n    }\n    throw $e;\n}","preventionTips":["Set PDF_COMMAND_TIMEOUT generously (2-5x your largest document's render time).","Smoke-test the configured command on a sample HTML in CI before deploying.","Keep the HTML input self-contained (no slow remote images) to avoid renderer hangs.","Run exports via a queue worker with its own generous time limit instead of a web request."],"tags":["php","process-timeout","symfony-process","pdf-export"],"backgroundTag":"process-timeout","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}