{"record":{"id":"b2e6ebc196852665","repo":"twentyhq/twenty","slug":"image-conversion-failed","errorCode":null,"errorMessage":"Image conversion failed","messagePattern":"Image 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":243,"sourceCode":"            \"--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\n    if visible_images:\n        with Image.open(visible_images[0]) as img:\n            placeholder_size = img.size\n    else:\n        placeholder_size = (1920, 1080)\n\n    for slide_num in range(1, total_slides + 1):\n        if slide_num in hidden_slides:\n            # Create placeholder image for hidden slide\n            placeholder_path = temp_dir / f\"hidden-{slide_num:03d}.jpg\"","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-server/src/engine/core-modules/code-interpreter/sandbox-scripts/pptx/thumbnail.py#L225-L261","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nif result.returncode != 0:\n    raise RuntimeError(\"Image conversion failed\")\n# after — include pdftoppm stderr for diagnosability\nif result.returncode != 0:\n    raise RuntimeError(\n        f\"Image conversion failed (rc={result.returncode}): \"\n        f\"{result.stderr.strip() or 'no stderr; verify pdftoppm is installed'}\"\n    )","handlingStrategy":"validation","validationCode":"import shutil, subprocess\n\ndef ensure_pdftoppm_available() -> None:\n    if shutil.which('pdftoppm') is None:\n        raise RuntimeError('pdftoppm (poppler-utils) not found on PATH; install poppler-utils')\n    r = subprocess.run(['pdftoppm', '-v'], capture_output=True, text=True)\n    if r.returncode != 0:\n        raise RuntimeError(f'pdftoppm present but broken: {r.stderr}')","typeGuard":"import shutil\n\ndef pdftoppm_ready() -> bool:\n    return shutil.which('pdftoppm') is not None","tryCatchPattern":"try:\n    generate_thumbnails(pptx_path, out_dir)\nexcept RuntimeError as e:\n    if 'Image conversion failed' in str(e):\n        if shutil.which('pdftoppm') is None:\n            raise RuntimeError('Install poppler-utils (pdftoppm) to rasterize PDFs.') from e\n        # verify the intermediate PDF is non-empty before retrying\n        if pdf_path.stat().st_size == 0:\n            raise RuntimeError('Intermediate PDF is empty; upstream soffice conversion failed silently.') from e\n        raise\n    raise","preventionTips":["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."],"tags":["python","pptx","code-interpreter","poppler","subprocess","thumbnail"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}