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
- Confirm `pdftoppm` is installed: `which pdftoppm` and `pdftoppm -v`.
- Inspect result.stderr by enriching the raise (see exampleFix) to get poppler's actual error.
- Verify the PDF produced in the previous step is non-empty and opens in a PDF reader.
- Ensure the DPI argument is a positive integer and the output prefix path is writable.
- 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
- Install poppler-utils alongside libreoffice in the sandbox image.
- Verify the intermediate PDF is non-empty before rasterizing.
- Pass a positive integer DPI; do not let user input reach pdftoppm unvalidated.
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
- PDF conversion failed
- Slide index {idx} out of range (0-{total_slides - 1})
- Duplicate key found in JSON: '{key}'
- Found {len(errors)} validation error(s)
- Found {len(overflow_errors)} overflow error(s) and {len(warn
AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12).
Data as JSON: /api/errors/b2e6ebc196852665.
Report an issue: GitHub.