odysseus-dev/odysseus · error · HTTPException
Extraction failed: {e}
Error message
Extraction failed: {e} What it means
500 from POST /api/document/{doc_id}/extract-pdf-text: the PDF was located, but _process_pdf(pdf_path, owner=user) raised while running text extraction (pypdf parse and/or the vision-language fallback). The underlying exception is logged as 'extract_pdf_text failed for <path>: <e>' and surfaced in the message.
Source
Thrown at routes/document/document_routes.py:533
doc = db.query(Document).filter(Document.id == doc_id).first()
if not doc:
raise HTTPException(404, "Document not found")
_verify_doc_owner(db, doc, user)
content = doc.current_content or ""
upload_id = find_source_upload_id(content)
if not upload_id:
raise HTTPException(400, "Document is not a PDF — no pdf_source marker found")
pdf_path = _locate_current_user_upload(request, upload_id, user)
if not pdf_path:
raise HTTPException(404, "Source PDF could not be located")
try:
body_text = strip_pdf_content_marker(_process_pdf(pdf_path, owner=user))
except Exception as e:
logger.error(f"extract_pdf_text failed for {pdf_path}: {e}")
raise HTTPException(500, f"Extraction failed: {e}")
if not body_text:
return {"ok": True, "id": doc_id, "extracted": False, "reason": "No readable content"}
# Preserve everything up through the title (front-matter marker +
# first H1) and replace the rest with the freshly extracted text.
head_re = re.compile(r'^(<!--[^>]+-->\s*\n+#[^\n]*\n+)', re.MULTILINE)
head_match = head_re.match(content)
head = head_match.group(1) if head_match else (content.splitlines()[0] + "\n\n# " + (doc.title or "PDF") + "\n\n")
doc.current_content = head + body_text.strip() + "\n"
doc.version_count = (doc.version_count or 1) + 1
db.add(DocumentVersion(
id=str(__import__("uuid").uuid4()),
document_id=doc_id,
version_number=doc.version_count,
content=doc.current_content,
summary="PDF text re-extracted (OCR)",
source="ocr",View on GitHub (pinned to f9235ebbf1)
Solutions
- Read the logged exception to identify whether pypdf parsing or the VL stage failed.
- Validate the PDF externally (qpdf --check, pdftotext) to confirm file integrity; re-upload a clean copy if corrupt.
- Decrypt/remove the password before importing if the file is protected.
- If the VL path is involved, check its API/dependency configuration and add size/page guards for huge files.
Example fix
# before
resp = requests.post(f"/api/document/{doc_id}/extract-pdf-text")
assert resp.ok # 500 Extraction failed: Stream has ended unexpectedly
# after
subprocess.run(['qpdf', '--check', pdf_path], check=True) # pre-validate
resp = requests.post(f"/api/document/{doc_id}/extract-pdf-text") Defensive patterns
Strategy: validation
Validate before calling
import subprocess
def pdf_is_parseable(path: str) -> bool:
return subprocess.run(['qpdf', '--check', path], capture_output=True).returncode == 0
if not pdf_is_parseable(path): raise ValueError('corrupt PDF — re-export before import') Type guard
function looksLikeValidPdf(file: File): boolean {
return file.size > 0 && (file.type === 'application/pdf' || file.name.endsWith('.pdf'));
} Try / catch
try { await api.post(`/api/document/${id}/extract-pdf-text`); }
catch (e) { if (e.status === 500 && /Extraction failed/.test(e.message)) { notify('PDF unreadable — please re-upload a clean copy'); return; } throw e; } Prevention
- Validate PDF integrity before import
- Reject encrypted PDFs early with a clear message
- Cap pages/size before invoking the VL extraction path
When it happens
Trigger: Corrupt or malformed PDF that pypdf cannot parse (EOF marker missing, broken xref); encrypted/password-protected PDF; the VL fallback failing on oversized/scanned pages; missing native dependency for the extraction pipeline.
Common situations: Users uploading truncated files (interrupted transfers); password-protected forms; very large scanned PDFs exhausting memory/time in the VL path; library version change altering pypdf behavior.
Related errors
- Upload failed: {e}
- Failed to create document for PDF
- d.error || 'Failed to create calendar'
- Failed to delete calendar
- Failed to list calendars
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/d48c21219c2de5ad.
Report an issue: GitHub.