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_slugView on GitHub (pinned to 80ffdda44c)
Solutions
- Use an object: {"mode": "in_app"} / {"mode": "origin"} / {"mode": "configured", "target_ref": ...}
- Omit or null the field to accept the in_app default
- 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
- Never send delivery as a bare mode string
- Serialize from a typed DeliverySpec object
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
- origin delivery requires the originating session
- source and schedule must be objects
- delivery.target_ref is required for configured delivery
- delivery.mode must be 'in_app', 'origin', or 'configured'
- timezone {tz!r} is not a recognized IANA timezone key
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/595eae5016428ded.
Report an issue: GitHub.