HKUDS/Vibe-Trading · error · ValueError

delivery must be an object

Error message

delivery must be an object

What it means

The draft's optional 'delivery' field, when present and non-null, must be a JSON object. A scalar, list, or non-null non-object value triggers this error (null/absent defaults to {'mode':'in_app'}).

Source

Thrown at agent/src/scheduled_research/service.py:130

def build_job_from_draft(
    draft: Mapping[str, Any],
    *,
    session_id: str | None = None,
    now_ms: int | None = None,
) -> ScheduledResearchJob:
    """Validate the public draft and build a persisted-model job."""
    now = int(time.time() * 1000) if now_ms is None else now_ms
    title = str(draft.get("title") or "").strip()
    if not title:
        raise ValueError("title is required")
    source = draft.get("source")
    schedule_spec = draft.get("schedule")
    delivery_spec = draft.get("delivery") or {"mode": "in_app"}
    if not isinstance(source, Mapping) or not isinstance(schedule_spec, Mapping):
        raise ValueError("source and schedule must be objects")
    if not isinstance(delivery_spec, Mapping):
        raise ValueError("delivery must be an object")

    source_type = str(source.get("kind") or "prompt")
    playbook_slug = None
    config: dict[str, Any] = {}
    if source_type == "prompt":
        prompt = str(source.get("prompt") or "").strip()
        if not prompt:
            raise ValueError("source.prompt is required")
    elif source_type == "playbook":
        playbook_slug = str(source.get("playbook_slug") or "").strip()
        if not playbook_slug:
            raise ValueError("source.playbook_slug is required")
        playbook = get_playbook(playbook_slug)
        variables = source.get("variables") or {}
        if not isinstance(variables, Mapping):
            raise ValueError("source.variables must be an object")
        prompt = playbook.render(variables)
        config["playbook"] = playbook_slug

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Use an object: {"mode": "in_app"} / {"mode": "origin"} / {"mode": "configured", "target_ref": ...}
  2. Omit or null the field to accept the in_app default
  3. Validate the draft payload shape client-side

Example fix

// before
{"delivery": "origin"}
// after
{"delivery": {"mode": "origin"}}
Defensive patterns

Strategy: validation

Validate before calling

from collections.abc import Mapping
d = draft.get("delivery")
assert d is None or isinstance(d, Mapping)

Type guard

from collections.abc import Mapping

def delivery_valid(d) -> bool:
    return d is None or (isinstance(d, Mapping) and not isinstance(d, (str, list)))

Try / catch

try:
    build_job_from_draft(draft)
except ValueError as e:
    if "delivery must be an object" in str(e):
        draft["delivery"] = {"mode": draft["delivery"]} if isinstance(draft["delivery"], str) else {"mode": "in_app"}

Prevention

When it happens

Trigger: Sending delivery: "telegram" or delivery: ["origin"] instead of {"mode": ...}; frontend serializing the wrong key shape.

Common situations: Refactors that flattened delivery to a mode string; version drift where older clients sent a string mode; typos in payload construction.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/595eae5016428ded. Report an issue: GitHub.