twentyhq/twenty · error · RuntimeError

Image conversion failed

Error message

Image conversion failed

What it means

Raised by thumbnail.py immediately after the PDF-conversion stage, when converting the intermediate PDF to JPEG images with `pdftoppm` (poppler-utils) fails. Unlike the PDF check, this one keys only on the subprocess return code, not on whether the JPEGs were produced. pdftoppm's stderr is captured but not surfaced in the message.

Source

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

            "--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
    if visible_images:
        with Image.open(visible_images[0]) as img:
            placeholder_size = img.size
    else:
        placeholder_size = (1920, 1080)

    for slide_num in range(1, total_slides + 1):
        if slide_num in hidden_slides:
            # Create placeholder image for hidden slide
            placeholder_path = temp_dir / f"hidden-{slide_num:03d}.jpg"

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Confirm `pdftoppm` is installed: `which pdftoppm` and `pdftoppm -v`.
  2. Inspect result.stderr by enriching the raise (see exampleFix) to get poppler's actual error.
  3. Verify the PDF produced in the previous step is non-empty and opens in a PDF reader.
  4. Ensure the DPI argument is a positive integer and the output prefix path is writable.
  5. Install poppler-utils in the sandbox image if missing.

Example fix

# before
if result.returncode != 0:
    raise RuntimeError("Image conversion failed")
# after — include pdftoppm stderr for diagnosability
if result.returncode != 0:
    raise RuntimeError(
        f"Image conversion failed (rc={result.returncode}): "
        f"{result.stderr.strip() or 'no stderr; verify pdftoppm is installed'}"
    )
Defensive patterns

Strategy: validation

Validate before calling

import shutil, subprocess

def ensure_pdftoppm_available() -> None:
    if shutil.which('pdftoppm') is None:
        raise RuntimeError('pdftoppm (poppler-utils) not found on PATH; install poppler-utils')
    r = subprocess.run(['pdftoppm', '-v'], capture_output=True, text=True)
    if r.returncode != 0:
        raise RuntimeError(f'pdftoppm present but broken: {r.stderr}')

Type guard

import shutil

def pdftoppm_ready() -> bool:
    return shutil.which('pdftoppm') is not None

Try / catch

try:
    generate_thumbnails(pptx_path, out_dir)
except RuntimeError as e:
    if 'Image conversion failed' in str(e):
        if shutil.which('pdftoppm') is None:
            raise RuntimeError('Install poppler-utils (pdftoppm) to rasterize PDFs.') from e
        # verify the intermediate PDF is non-empty before retrying
        if pdf_path.stat().st_size == 0:
            raise RuntimeError('Intermediate PDF is empty; upstream soffice conversion failed silently.') from e
        raise
    raise

Prevention

When it happens

Trigger: pdftoppm (poppler-utils) is not installed or not on PATH; the intermediate PDF is corrupt or zero-byte (so the prior 'PDF conversion failed' guard somehow passed but the PDF is unusable); the DPI value passed is invalid or zero; the temp dir is not writable; poppler lacks the JPEG encoder or a needed dependency.

Common situations: Sandbox image with libreoffice but without poppler-utils; a PDF that soffice wrote but poppler cannot parse; permissions on /tmp inside the sandbox; concurrent runs exhausting file descriptors.

Related errors


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