twentyhq/twenty · error · RuntimeError

PDF conversion failed

Error message

PDF conversion failed

What it means

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.

Source

Thrown at packages/twenty-server/src/engine/core-modules/code-interpreter/sandbox-scripts/pptx/thumbnail.py:233

    pdf_path = temp_dir / f"{pptx_path.stem}.pdf"

    # Convert to PDF
    print("Converting to PDF...")
    result = subprocess.run(
        [
            "soffice",
            "--headless",
            "--convert-to",
            "pdf",
            "--outdir",
            str(temp_dir),
            str(pptx_path),
        ],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0 or not pdf_path.exists():
        raise RuntimeError("PDF conversion failed")

    # Convert PDF to images
    print(f"Converting to images at {dpi} DPI...")
    result = subprocess.run(
        ["pdftoppm", "-jpeg", "-r", str(dpi), str(pdf_path), str(temp_dir / "slide")],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
        raise RuntimeError("Image conversion failed")

    visible_images = sorted(temp_dir.glob("slide-*.jpg"))

    # Create full list with placeholders for hidden slides
    all_images = []
    visible_idx = 0

    # Get placeholder dimensions from first visible slide

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Confirm `soffice` is installed and on PATH: `which soffice` and `soffice --version`.
  2. Capture and inspect result.stderr — replace the bare raise with one that appends `result.stderr` to see soffice's actual error.
  3. Clear a stale LibreOffice profile lock: remove `~/.config/libreoffice/.lock` (or run soffice with `-env:UserInstallation=file:///tmp/lo-profile` to isolate the profile).
  4. Ensure the temp dir is writable and has free space, and that pptx_path is a valid, unencrypted .pptx.
  5. Run soffice serially (it is not concurrency-safe per profile) if multiple thumbnails generate in parallel.

Example fix

# before
if result.returncode != 0 or not pdf_path.exists():
    raise RuntimeError("PDF conversion failed")
# after — surface soffice's stderr so the cause is not lost
if result.returncode != 0 or not pdf_path.exists():
    raise RuntimeError(
        f"PDF conversion failed (rc={result.returncode}): "
        f"{result.stderr.strip() or 'no stderr; check soffice install and path'}"
    )
Defensive patterns

Strategy: validation

Validate before calling

import shutil, subprocess

def ensure_soffice_available() -> None:
    if shutil.which('soffice') is None:
        raise RuntimeError('soffice (LibreOffice) not found on PATH; install libreoffice')
    # also confirm it can start a headless convert
    r = subprocess.run(['soffice', '--version'], capture_output=True, text=True)
    if r.returncode != 0:
        raise RuntimeError(f'soffice present but broken: {r.stderr}')

Type guard

import shutil

def soffice_ready() -> bool:
    return shutil.which('soffice') is not None

Try / catch

try:
    generate_thumbnails(pptx_path, out_dir)
except RuntimeError as e:
    if 'PDF conversion failed' in str(e):
        # most common causes: missing soffice, stale profile lock, or corrupt deck
        if shutil.which('soffice') is None:
            raise RuntimeError('Install libreoffice (soffice) to generate thumbnails.') from e
        # retry once with an isolated profile to dodge the lock
        generate_thumbnails(pptx_path, out_dir, soffice_profile='/tmp/lo-isolated')
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12). Data as JSON: /api/errors/e3da6cc82525ff5d. Report an issue: GitHub.