zylon-ai/private-gpt · error · RuntimeError

Ghostscript failed for {file_path.name}: {stderr_text}

Error message

Ghostscript failed for {file_path.name}: {stderr_text}

What it means

VisionReader shells out to Ghostscript (gs) with a 120s timeout to rasterize a document (e.g. PDF) to PNG pages; if the subprocess exits non-zero, the reader raises RuntimeError including the file name and Ghostscript's stderr text. The stderr payload is the actual diagnosis — common causes are a missing/corrupt input file, an unsupported or encrypted PDF, or a broken Ghostscript installation.

Source

Thrown at private_gpt/components/readers/vision/vision_reader.py:76

            cmd = [
                "gs",
                "-sDEVICE=png16m",
                f"-o{output_pattern}",
                f"-r{resolution}",
                "-dNOPAUSE",
                "-dBATCH",
                str(file_path),
            ]

            try:
                result = subprocess.run(cmd, capture_output=True, timeout=120)
                if result.returncode != 0:
                    stderr_text = (
                        result.stderr.decode("utf-8", errors="replace")
                        if result.stderr
                        else "No error output"
                    )
                    raise RuntimeError(
                        f"Ghostscript failed for {file_path.name}: {stderr_text}"
                    )
            except subprocess.TimeoutExpired:
                raise RuntimeError(
                    f"Ghostscript timed out for {file_path.name}"
                ) from None

            png_files = sorted(temp_path.glob("page-*.png"))
            for png_file in png_files:
                pil_image = Image.open(png_file)
                buffer = io.BytesIO()
                pil_image.save(buffer, format="JPEG", quality=85, optimize=True)
                images.append(buffer.getvalue())

        logger.info(
            "Rendered %d pages from %s (gs, %ddpi)",
            len(images),
            file_path.name,

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Read the stderr text in the message — it identifies the exact Ghostscript failure (e.g. 'This file requires a password', 'unreadable file').
  2. Test the file manually: `gs -dNOPAUSE -dBATCH -sDEVICE=png16m -r<dpi> -o out-%d.png file.pdf`.
  3. Ensure Ghostscript is installed and on PATH (`gs --version`) inside the container/host running ingestion.
  4. For encrypted PDFs, decrypt (qpdf --decrypt) or exclude them from vision ingestion.

Example fix

# before
images = vision_reader._render_to_images(Path("locked.pdf"))
# RuntimeError: Ghostscript failed for locked.pdf: ... password ...

# after
# shell: qpdf --decrypt locked.pdf unlocked.pdf
images = vision_reader._render_to_images(Path("unlocked.pdf"))
Defensive patterns

Strategy: try-catch

Validate before calling

import shutil, subprocess

def ghostscript_ready() -> bool:
    gs = shutil.which("gs")
    if not gs:
        return False
    return subprocess.run([gs, "--version"], capture_output=True).returncode == 0

Try / catch

try:
    images = vision_reader.render(Path(pdf))
except RuntimeError as e:
    if "Ghostscript failed" in str(e):
        logger.error("Render failed, gs stderr: %s", e)
        quarantine(pdf)
    raise

Prevention

When it happens

Trigger: Ingesting a document through VisionReader where `gs` returns a non-zero exit code — encrypted/password-protected PDFs, malformed PDFs, missing gs binary producing a shell error, or output-path permission issues.

Common situations: Password-protected PDF exports; corrupt/truncated PDF downloads; containers without the ghostscript package installed or not on PATH; a full or read-only temp directory; version mismatch between gs and the PDF's features.

Related errors


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/9f9e35e87d1ad445. Report an issue: GitHub.