{"record":{"id":"e3da6cc82525ff5d","repo":"twentyhq/twenty","slug":"pdf-conversion-failed","errorCode":null,"errorMessage":"PDF conversion failed","messagePattern":"PDF conversion failed","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"packages/twenty-server/src/engine/core-modules/code-interpreter/sandbox-scripts/pptx/thumbnail.py","lineNumber":233,"sourceCode":"    pdf_path = temp_dir / f\"{pptx_path.stem}.pdf\"\n\n    # Convert to PDF\n    print(\"Converting to PDF...\")\n    result = subprocess.run(\n        [\n            \"soffice\",\n            \"--headless\",\n            \"--convert-to\",\n            \"pdf\",\n            \"--outdir\",\n            str(temp_dir),\n            str(pptx_path),\n        ],\n        capture_output=True,\n        text=True,\n    )\n    if result.returncode != 0 or not pdf_path.exists():\n        raise RuntimeError(\"PDF conversion failed\")\n\n    # Convert PDF to images\n    print(f\"Converting to images at {dpi} DPI...\")\n    result = subprocess.run(\n        [\"pdftoppm\", \"-jpeg\", \"-r\", str(dpi), str(pdf_path), str(temp_dir / \"slide\")],\n        capture_output=True,\n        text=True,\n    )\n    if result.returncode != 0:\n        raise RuntimeError(\"Image conversion failed\")\n\n    visible_images = sorted(temp_dir.glob(\"slide-*.jpg\"))\n\n    # Create full list with placeholders for hidden slides\n    all_images = []\n    visible_idx = 0\n\n    # Get placeholder dimensions from first visible slide","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-server/src/engine/core-modules/code-interpreter/sandbox-scripts/pptx/thumbnail.py#L215-L251","documentation":"Raised by thumbnail.py after invoking LibreOffice (`soffice --headless --convert-to pdf`) to render a .pptx to PDF as the first stage of thumbnail generation. The check combines the subprocess return code and the existence of the expected PDF on disk; failure of either trips the error. The subprocess's stderr is captured but not included in the message, so the cause must be read from result.stderr.","triggerScenarios":"soffice is not installed or not on PATH in the sandbox; soffice exits non-zero because the .pptx is corrupt or password-protected; a concurrent soffice instance holds the profile lock (LibreOffice is single-instance per user profile); the output directory is not writable; or the conversion 'succeeds' (rc 0) but writes the PDF to an unexpected path so pdf_path does not exist.","commonSituations":"Sandbox image missing the libreoffice package; a previous soffice process left a stale ~/.config/libreoffice lock; running thumbnail generation on a deck with embedded OLE objects soffice cannot render; disk-full in the temp dir.","solutions":["Confirm `soffice` is installed and on PATH: `which soffice` and `soffice --version`.","Capture and inspect result.stderr — replace the bare raise with one that appends `result.stderr` to see soffice's actual error.","Clear a stale LibreOffice profile lock: remove `~/.config/libreoffice/.lock` (or run soffice with `-env:UserInstallation=file:///tmp/lo-profile` to isolate the profile).","Ensure the temp dir is writable and has free space, and that pptx_path is a valid, unencrypted .pptx.","Run soffice serially (it is not concurrency-safe per profile) if multiple thumbnails generate in parallel."],"exampleFix":"# before\nif result.returncode != 0 or not pdf_path.exists():\n    raise RuntimeError(\"PDF conversion failed\")\n# after — surface soffice's stderr so the cause is not lost\nif result.returncode != 0 or not pdf_path.exists():\n    raise RuntimeError(\n        f\"PDF conversion failed (rc={result.returncode}): \"\n        f\"{result.stderr.strip() or 'no stderr; check soffice install and path'}\"\n    )","handlingStrategy":"validation","validationCode":"import shutil, subprocess\n\ndef ensure_soffice_available() -> None:\n    if shutil.which('soffice') is None:\n        raise RuntimeError('soffice (LibreOffice) not found on PATH; install libreoffice')\n    # also confirm it can start a headless convert\n    r = subprocess.run(['soffice', '--version'], capture_output=True, text=True)\n    if r.returncode != 0:\n        raise RuntimeError(f'soffice present but broken: {r.stderr}')","typeGuard":"import shutil\n\ndef soffice_ready() -> bool:\n    return shutil.which('soffice') is not None","tryCatchPattern":"try:\n    generate_thumbnails(pptx_path, out_dir)\nexcept RuntimeError as e:\n    if 'PDF conversion failed' in str(e):\n        # most common causes: missing soffice, stale profile lock, or corrupt deck\n        if shutil.which('soffice') is None:\n            raise RuntimeError('Install libreoffice (soffice) to generate thumbnails.') from e\n        # retry once with an isolated profile to dodge the lock\n        generate_thumbnails(pptx_path, out_dir, soffice_profile='/tmp/lo-isolated')\n    else:\n        raise","preventionTips":["Bake libreoffice into the sandbox image; do not assume the host provides it.","Run soffice with an isolated UserInstallation dir per process to avoid profile lock contention.","Pre-validate the .pptx opens cleanly (e.g. Presentation() load succeeds) before invoking soffice."],"tags":["python","pptx","code-interpreter","libreoffice","subprocess","thumbnail"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}