zylon-ai/private-gpt · error · RuntimeError

Ghostscript timed out for {file_path.name}

Error message

Ghostscript timed out for {file_path.name}

What it means

VisionReader runs Ghostscript with subprocess.run(..., timeout=120); a TimeoutExpired is caught and re-raised as RuntimeError('Ghostscript timed out for <file>') with the original suppressed (`from None`). It means the rasterization did not finish within the hard-coded 120-second budget — typically very large or pathologically complex documents, or an overloaded/starved machine.

Source

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

                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,
            resolution,
        )
        return images

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Split the document into smaller parts (per-chapter/per-100-pages PDFs) and ingest them separately.
  2. Free up CPU for the worker or raise container CPU limits so gs finishes within budget.
  3. Reduce the render DPI if the pipeline exposes it, cutting rasterization time.
  4. If you control the code, make the 120s timeout configurable (it is hard-coded at vision_reader.py:76) and set a higher value for known-large corpora.

Example fix

# before
# vision_reader.py (hard-coded)
result = subprocess.run(cmd, capture_output=True, timeout=120)

# after
result = subprocess.run(cmd, capture_output=True, timeout=settings.transformation.vision_documents.gs_timeout)
Defensive patterns

Strategy: retry

Validate before calling

from pypdf import PdfReader

def estimate_render_cost(pdf_path: str) -> int:
    return len(PdfReader(pdf_path).pages)  # budget ~ pages/sec of gs throughput

Try / catch

for attempt, chunk in enumerate(split_pdf(pdf, max_pages=200)):
    try:
        images.extend(vision_reader.render(chunk))
    except RuntimeError as e:
        if "timed out" in str(e) and attempt == 0:
            chunk = split_pdf(chunk, max_pages=50)[0]  # retry smaller
            continue
        raise

Prevention

When it happens

Trigger: Vision ingestion of a document whose Ghostscript rasterization exceeds 120s — thousands of pages, huge page dimensions, complex vector graphics, or a CPU-throttled container slowing gs below its usual speed.

Common situations: Bulk-ingesting large scanned PDFs; containers with tight CPU limits where gs is throttled; documents with extremely high-resolution embedded images; shared CI runners under load.

Understand the failure class

Related errors


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