odysseus-dev/odysseus · error · HTTPException

Document has no source email — cannot reply

Error message

Document has no source email — cannot reply

What it means

Guard in prepare_signed_reply: the document being replied to has no linked source email, so there is no original message to construct a reply from (no In-Reply-To/References headers or sender to address). The signed-reply flow only works for documents that were imported from or attached to an email; fires when replying to manually created or non-email-sourced documents.

Solutions

  1. Only use reply on documents created from an email source.
  2. Open the original email and reply from there instead.
Defensive patterns

Strategy: validation

When it happens

Trigger: Triggered when the corresponding server-side validation or runtime check at the recorded location rejects the request or operation and returns this error message to the caller.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at routes/document/document_routes.py:1651

        from src.pdf_forms import fill_fields, stamp_signatures, stamp_annotations
        from core.database import Signature
        # COMPOSE_UPLOADS_DIR lives in email_routes — re-derive here so we
        # don't import from a routes file (cycle-prone). Same env override
        # as email_routes (ODYSSEUS_MAIL_ATTACHMENTS_DIR).
        from pathlib import Path as _Path
        _COMPOSE_DIR = _Path(MAIL_ATTACHMENTS_DIR) / "_compose"
        _COMPOSE_DIR.mkdir(parents=True, exist_ok=True)

        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)

            if not (doc.source_email_uid and doc.source_email_folder):
                raise HTTPException(400, "Document has no source email — cannot reply")

            # 1) Build the flattened PDF (same pipeline as export_pdf)
            upload_id = find_source_upload_id(doc.current_content or "")
            if not upload_id:
                raise HTTPException(400, "Document is not linked to a source PDF")
            pdf_path = _locate_current_user_upload(request, upload_id, user)
            if not pdf_path:
                raise HTTPException(404, f"Source PDF {upload_id} not found")

            schema = load_field_sidecar(pdf_path) or []
            sig_field_names = {f["name"] for f in schema if f.get("type") == "signature"}
            all_values = parse_markdown_to_values(doc.current_content or "")
            text_values: dict = {}
            sig_ids: dict[str, str] = {}
            for name, raw in all_values.items():
                if name in sig_field_names and isinstance(raw, str) and raw.startswith("signature:"):
                    sig_ids[name] = raw[len("signature:"):].strip()
                elif name not in sig_field_names:

View on GitHub (pinned to f9235ebbf1)