odysseus-dev/odysseus · info · HTTPException

Document is not a PDF — no pdf_source marker found

Error message

Document is not a PDF — no pdf_source marker found

What it means

400 from POST /api/document/{doc_id}/extract-pdf-text: the document exists and is owned by the caller, but its current_content contains no pdf_source marker that find_source_upload_id can parse. The endpoint only works on documents originally imported from a PDF; text-native or otherwise-created docs have no linked upload.

Source

Thrown at routes/document/document_routes.py:523

        Lets the AI see PDF contents for old docs that were imported before
        text extraction was wired, plus for scanned/image-only PDFs where the
        VL model picks up text the basic pypdf path missed."""
        import re
        from src.document_processor import _process_pdf, strip_pdf_content_marker
        from src.pdf_form_doc import find_source_upload_id

        user = get_current_user(request)
        db = SessionLocal()
        try:
            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)

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Only offer/attempt extraction for docs whose content contains the pdf_source marker (or that were created by import-pdf).
  2. For legacy PDF docs without the marker, re-import the PDF to create a properly linked doc.
  3. If content edits must preserve provenance, keep the marker intact when rewriting doc content.
  4. Handle the 400 as 'not applicable' in automation rather than as a failure.

Example fix

// before
await api.post(`/api/document/${doc.id}/extract-pdf-text`); // 400 on text docs
// after
const isPdfDoc = /<!--\s*pdf_source/.test(doc.current_content);
if (isPdfDoc) await api.post(`/api/document/${doc.id}/extract-pdf-text`);
Defensive patterns

Strategy: type-guard

Validate before calling

const isPdfDoc = /<!--\s*pdf_source[^>]*-->/.test(doc.current_content ?? '');
if (!isPdfDoc) return; // skip extraction for non-PDF docs

Type guard

function isPdfBackedDoc(doc: { current_content?: string | null }): boolean {
  return /pdf_source/.test(doc.current_content ?? '');
}

Try / catch

try { await api.post(`/api/document/${id}/extract-pdf-text`); }
catch (e) { if (e.status === 400 && /not a PDF/.test(e.message)) return; /* expected for text docs */ throw e; }

Prevention

When it happens

Trigger: Running extraction on a markdown/text document created directly (POST /api/documents); a PDF-imported doc whose content was later fully overwritten by an edit that dropped the marker; very old docs imported before the pdf_source marker convention existed.

Common situations: Bulk 'extract all' scripts hitting mixed libraries; UI offering the extract action on every doc regardless of origin; content-rewrite tools stripping HTML comments (the marker is an HTML comment) during transforms.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/58a9561a21868185. Report an issue: GitHub.