odysseus-dev/odysseus · error · HTTPException

Documents integration is not available

Error message

Documents integration is not available

What it means

Raised as HTTP 503 by POST /api/codex/emails/draft-document when documents_create_endpoint is None — setup_codex_routes could not resolve the documents-create endpoint on the document router it was given. The route composes email drafting with document creation; without the documents integration mounted it can only report unavailability. Note it fires after the scope and owner checks, so auth was fine.

Source

Thrown at routes/codex_routes.py:345

            lines.append(f"Cc: {cc}")
        if bcc:
            lines.append(f"Bcc: {bcc}")
        lines.append(f"Subject: {subject}")
        if in_reply_to:
            lines.append(f"In-Reply-To: {in_reply_to}")
        if references:
            lines.append(f"References: {references}")
        lines.extend(["---", body_text])
        return "\n".join(lines).rstrip() + "\n"

    @router.post("/emails/draft-document")
    async def codex_email_draft_document(request: Request, body: dict[str, Any] = Body(default_factory=dict)):
        owner = _scope_owner(request, EMAIL_DRAFT_SCOPES)
        docs_owner = _scope_owner_all(request, DOCS_WRITE_SCOPES)
        if docs_owner != owner:
            raise HTTPException(403, "API token owner mismatch")
        if documents_create_endpoint is None:
            raise HTTPException(503, "Documents integration is not available")
        from routes.document_routes import DocumentCreate

        subject = str(body.get("subject") or "Email draft").strip() or "Email draft"
        title = str(body.get("title") or subject).strip() or "Email draft"
        req = DocumentCreate(
            session_id=body.get("session_id"),
            title=title,
            language="email",
            content=_email_draft_document_content(body),
        )
        result = await _as_owner(request, owner, documents_create_endpoint, request, req)
        if isinstance(result, dict):
            result = dict(result)
            result["draft_type"] = "document"
            result["send_required_confirmation"] = True
        return result

    @router.post("/emails/draft")

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Confirm POST on the documents create route works directly; if not, enable/mount the document integration.
  2. Ensure setup_codex_routes receives a built document_router at startup.
  3. Align app version so codex_routes matches document_routes' current paths.
  4. If documents are intentionally disabled, use POST /api/codex/emails/draft instead (email-only draft).
Defensive patterns

Strategy: try-catch

Validate before calling

const docsOk = await fetch('/api/documents', {method: 'OPTIONS'}).then(r => r.status !== 404);
if (!docsOk) useEmailOnlyDraft();

Try / catch

try { r = await codexEmailDraftDocument(body) } catch (e) { if (e.status === 503) { return codexEmailDraft(body) /* email-only fallback */ } throw }

Prevention

When it happens

Trigger: Calling /emails/draft-document when the document router was not passed to setup_codex_routes or its create route path changed so _find_endpoint returned None.

Common situations: Documents feature disabled in deployment; version skew after a document-routes refactor renamed the create endpoint; custom app assembly omitting the document router.

Related errors


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