odysseus-dev/odysseus · error · ValueError

malformed compound UID: missing base before ::

Error message

malformed compound UID: missing base before ::

What it means

ValueError raised by _resolve_base_uid when the uid contains '::' but the base segment before it is empty, e.g. '::20260814'. The compound format is '{base_uid}::{date_suffix}'; an empty base has no resolvable series, so the helper refuses it.

Source

Thrown at routes/calendar_routes.py:147

    if not stem:
        stem = "calendar"
    return f"{stem[:128]}.ics"


def _resolve_base_uid(uid: str) -> str:
    """Extract the base series UID from a compound occurrence UID.

    Compound UIDs have the form ``{base_uid}::{date_suffix}``.
    For plain UIDs (no ``::``), returns the UID unchanged.
    """
    if not uid:
        raise ValueError("empty uid")
    idx = uid.find("::")
    if idx == -1:
        return uid       # plain UID — no suffix
    base = uid[:idx]
    if not base:
        raise ValueError("malformed compound UID: missing base before ::")
    return base


async def _push_caldav_event_after_commit(owner: str, uid: str, action: str):
    """Best-effort CalDAV write-through. Local writes stay authoritative if
    the remote server is unreachable; pending flags let /sync retry later."""
    try:
        result = {"ok": True}
        if action == "create":
            from src.caldav_sync import push_event_create
            result = await push_event_create(owner, uid)
        elif action == "update":
            from src.caldav_sync import push_event_update
            result = await push_event_update(owner, uid)
        elif action == "delete":
            from src.caldav_sync import push_event_delete
            result = await push_event_delete(owner, uid)
        if result and not result.get("ok") and not result.get("skipped"):

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Fix the uid construction site to guarantee a non-empty base before appending '::'.
  2. Sanitize incoming uids at the API boundary: reject ones matching /^::/ with a 400.
  3. Repair corrupted rows in the events table rather than working around the ValueError.

Example fix

// before
const uid = `${base}::${date}`;   // base may be ''

// after
if (!base) throw new Error('base uid required');
const uid = `${base}::${date}`;
Defensive patterns

Strategy: type-guard

Validate before calling

const okCompound = (uid) => !uid.includes('::') || uid.split('::')[0].length > 0;
if (!okCompound(uid)) return badRequest('malformed uid');

Type guard

const isWellFormedUid = (u: string): boolean =>
  u.length > 0 && !u.startsWith('::');

Try / catch

catch (e) { if (e instanceof ValueError && /malformed/.test(e.message)) { rejectPayload(); } }

Prevention

When it happens

Trigger: Passing a malformed compound uid like '::2026-08-14' or ' ::x' (whitespace-only base) into recurrence resolution or an occurrence-edit endpoint.

Common situations: Client-side uid concatenation bug (base variable empty when building `${base}::${date}`); imported/corrupted CalDAV data with mangled UIDs; hand-crafted test payloads.

Understand the failure class

Related errors


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