odysseus-dev/odysseus · error · HTTPException

Document PDF marker references an upload you do not own

Error message

Document PDF marker references an upload you do not own

What it means

Raised (HTTP 400) by _assert_pdf_marker_upload_owned when a document's content contains a pdf_source marker (extracted via src.pdf_form_doc.find_source_upload_id) whose referenced upload cannot be resolved for the requesting user through _resolve_user_upload_path (checks the upload handler plus the app's auth_manager). It prevents persisting a document that embeds another user's uploaded PDF — an authorization check at the content level, not the row level.

Source

Thrown at routes/document/document_helpers.py:203


def _assert_pdf_marker_upload_owned(
    request: Request,
    content: str,
    user: Optional[str],
    upload_handler: Any,
) -> None:
    """Reject document content whose pdf_source marker points at another user's upload."""
    if upload_handler is None:
        return
    from src.pdf_form_doc import find_source_upload_id

    upload_id = find_source_upload_id(content or "")
    if not upload_id:
        return
    auth_manager = getattr(getattr(request.app, "state", None), "auth_manager", None)
    if not _resolve_user_upload_path(upload_handler, upload_id, user, auth_manager):
        raise HTTPException(
            400,
            "Document PDF marker references an upload you do not own",
        )


def _derive_title(content: str) -> str:
    """Derive a title from document content."""
    import re
    if not isinstance(content, str):
        return "Untitled"
    text = content.strip()
    if not text:
        return "Untitled"

    # Markdown header
    md = re.match(r'^#{1,3}\s+(.+)', text, re.MULTILINE)
    if md:
        title = md.group(1).strip()

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Re-upload the PDF under your own account and let the app regenerate the marker, instead of reusing a copied marker.
  2. If the upload was deleted, restore it (or re-import the PDF) so the ID resolves again for you.
  3. Strip stale markers from the content before saving if the PDF linkage is no longer needed.
Defensive patterns

Strategy: validation

Validate before calling

// Before saving, confirm every pdf_source marker in the content resolves for this user
const markerIds = [...content.matchAll(/pdf_source[:=]\s*([\w-]+)/g)].map(m => m[1]);
for (const uploadId of new Set(markerIds)) {
  const ok = await head(`/api/upload/${uploadId}`); // 200 only if owned & present
  if (!ok) throw new Error(`Upload ${uploadId} unavailable — re-upload the PDF`);
}

Try / catch

try { await post('/api/document', { content }); } catch (e) { if (e.status === 400 && /PDF marker/.test(e.message)) { stripStaleMarkers(content); return saveAgain(); } throw e; }

Prevention

When it happens

Trigger: Creating or updating a document whose content string carries a pdf_source marker pointing at an upload_id that was deleted, expired, or belongs to a different user. Commonly happens when copying/cloning document content (with its markers) across accounts, or when an upload was garbage-collected between load and save.

Common situations: User pastes raw document markdown including an internal pdf_source marker from someone else's doc; upload retention job removed the referenced file; cloning between users copies markers verbatim.

Related errors


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