HKUDS/Vibe-Trading · error · ValueError

source and schedule must be objects

Error message

source and schedule must be objects

What it means

The draft's 'source' and 'schedule' fields must both be JSON objects (Mappings). One of them is missing, null, or a scalar/array, so the builder cannot read kind/prompt or expression/timezone from it.

Source

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

    return channel, target, None, "当前会话"


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")

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Wrap values in objects: source={"kind":"prompt","prompt":"..."} and schedule={"expression":"0 9 * * *"}
  2. Check the current draft schema in service.build_job_from_draft
  3. Add a client-side schema validator before submit

Example fix

// before
{"source": "summarize HN", "schedule": "0 9 * * *"}
// after
{"source": {"kind": "prompt", "prompt": "summarize HN"}, "schedule": {"expression": "0 9 * * *"}}
Defensive patterns

Strategy: type-guard

Validate before calling

from collections.abc import Mapping
assert isinstance(draft.get("source"), Mapping) and isinstance(draft.get("schedule"), Mapping)

Type guard

from collections.abc import Mapping

def draft_source_schedule_valid(d: dict) -> bool:
    return isinstance(d.get("source"), Mapping) and isinstance(d.get("schedule"), Mapping)

Try / catch

try:
    build_job_from_draft(draft)
except ValueError as e:
    return schema_error(str(e))  # map to 400 for the client

Prevention

When it happens

Trigger: Sending draft with source as a string prompt instead of {kind:'prompt',prompt:...}; schedule as a cron string instead of {expression:...}; either field omitted or null.

Common situations: API shape mismatch after version upgrade; clients simplifying the nested schema; docs examples that show flattened forms.

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/c6333e63811aa790. Report an issue: GitHub.