HKUDS/Vibe-Trading · error · ValueError

source.kind must be 'prompt' or 'playbook'

Error message

source.kind must be 'prompt' or 'playbook'

What it means

draft.source.kind must be exactly 'prompt' or 'playbook'. Any other string (or a kind that is neither, e.g. 'template', 'query', 'workflow') falls into the else branch and raises.

Source

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

    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
    else:
        raise ValueError("source.kind must be 'prompt' or 'playbook'")

    expression = str(schedule_spec.get("expression") or "").strip()
    timezone = schedule_spec.get("timezone")
    validate_schedule(expression)
    if is_interval_schedule(expression):
        validate_timezone_shape(timezone)
    else:
        validate_timezone(timezone)
    next_run_at = now
    if timezone is not None and not is_interval_schedule(expression):
        next_run_at = next_due(expression, now, timezone)

    end_at = _parse_end_at(draft.get("end_at"))
    if end_at is not None and end_at <= now:
        raise ValueError("end_at must be in the future")
    if end_at is not None and next_run_at > end_at:
        raise ValueError("the first scheduled run occurs after end_at")

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Use 'prompt' or 'playbook' exactly (lowercase)
  2. Omit kind entirely — it defaults to 'prompt'
  3. Align client and server on the supported kind enum after upgrades

Example fix

// before
{"source": {"kind": "Playbook", "playbook_slug": "x"}}
// after
{"source": {"kind": "playbook", "playbook_slug": "x"}}
Defensive patterns

Strategy: validation

Validate before calling

assert draft.get("source", {}).get("kind", "prompt") in {"prompt", "playbook"}

Type guard

def source_kind_valid(src: dict) -> bool:
    return str(src.get("kind") or "prompt") in {"prompt", "playbook"}

Try / catch

try:
    propose_create(draft)
except ValueError as e:
    if "source.kind" in str(e):
        draft["source"]["kind"] = "prompt"  # normalize fallback

Prevention

When it happens

Trigger: source={"kind":"workflow"}; typo like 'promt' or 'Playbook' (case-sensitive); passing a kind value from a newer/older schema.

Common situations: Client/server version drift adding new source kinds; user-typed kind values; case mismatches from locale-aware lowercasing.

Related errors


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