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
- Split the document into smaller parts (per-chapter/per-100-pages PDFs) and ingest them separately.
- Free up CPU for the worker or raise container CPU limits so gs finishes within budget.
- Reduce the render DPI if the pipeline exposes it, cutting rasterization time.
- 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
- Split large PDFs into bounded page counts before vision ingestion.
- Give ingestion workers enough CPU so gs finishes within the 120s budget.
- Monitor per-file render duration and pre-emptively chunk anything unusually slow.
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Ghostscript failed for {file_path.name}: {stderr_text}
- Server did not become ready within {_HEALTH_TIMEOUT}s
- OVERLOADED_CONDENSATION_ERROR
- Failed to describe images in the message.
- LLM does not support structured chat.
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/5e364d7fd903b214.
Report an issue: GitHub.