odysseus-dev/odysseus · error · HTTPException

Referenced upload is no longer available: {missing_id}

Error message

Referenced upload is no longer available: {missing_id}

What it means

HTTP 409 raised when _reserve_calendar_references finds an upload id referenced by the request payload that cannot be reserved for the caller: reserve_upload_references extracts internal upload ids (URLs, PDF source refs, attachment lines) from the payload values and owner-checks them against the upload handler. A missing id means the referenced upload was deleted, expired, or belongs to another user, so the calendar write is rejected before persisting dangling references.

Source

Thrown at routes/calendar_routes.py:815

        for d in results:
            d["truncated"] = True

    return results


# ── Routes ──

def setup_calendar_routes(upload_handler=None) -> APIRouter:
    router = APIRouter(prefix="/api/calendar", tags=["calendar"])

    def _reserve_calendar_uploads(request: Request, *values) -> None:
        missing_id = reserve_upload_references(
            upload_handler,
            effective_user(request),
            *values,
        )
        if missing_id:
            raise HTTPException(409, f"Referenced upload is no longer available: {missing_id}")

    # ── CalDAV multi-account helpers ─────────────────────────────────────────

    def _get_caldav_accounts(owner: str) -> list:
        from src.caldav_sync import _load_caldav_accounts
        return _load_caldav_accounts(owner)

    def _save_caldav_accounts(owner: str, accounts: list) -> None:
        from routes.prefs_routes import _load_for_user, _save_for_user
        prefs = _load_for_user(owner) or {}
        prefs["caldav_accounts"] = accounts
        prefs.pop("caldav", None)
        _save_for_user(owner, prefs)

    # ── CalDAV config routes (backward-compat single-account API) ────────────

    @router.get("/config")
    async def get_config(request: Request):

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Remove or re-upload the attachment named in the 409 message, then retry.
  2. If the upload should exist, verify it belongs to the same owner and was not cleaned up.
  3. When copying events across users/instances, strip internal upload references or re-attach the files under the target owner.
Defensive patterns

Strategy: validation

Validate before calling

const UPLOAD_RE = /[internal upload url pattern]/; // extract ids like the server does
function referencedUploads(text) { return [...text.matchAll(UPLOAD_RE)].map(m => m[1]); }
// verify each id still exists and is owned by the caller before POSTing the event

Try / catch

catch (e) {
  if (e.status === 409) { const id = e.message.split(': ').pop(); stripReference(id); retry(); }
  else throw e;
}

Prevention

When it happens

Trigger: Creating/updating a calendar event whose description or attachment field references /uploads/... or an attachment line for a file that no longer exists or is owned by another account. The 409 body names the first offending id.

Common situations: Copy-pasting event content with an old upload URL between users or instances; upload expired/cleaned by retention policy; multi-user deployment where uploads are owner-scoped.

Related errors


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