{"record":{"id":"26ba62d6b473bfa5","repo":"HKUDS/Vibe-Trading","slug":"source-kind-must-be-prompt-or-playbook","errorCode":null,"errorMessage":"source.kind must be 'prompt' or 'playbook'","messagePattern":"source\\.kind must be 'prompt' or 'playbook'","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"agent/src/scheduled_research/service.py","lineNumber":150,"sourceCode":"    source_type = str(source.get(\"kind\") or \"prompt\")\n    playbook_slug = None\n    config: dict[str, Any] = {}\n    if source_type == \"prompt\":\n        prompt = str(source.get(\"prompt\") or \"\").strip()\n        if not prompt:\n            raise ValueError(\"source.prompt is required\")\n    elif source_type == \"playbook\":\n        playbook_slug = str(source.get(\"playbook_slug\") or \"\").strip()\n        if not playbook_slug:\n            raise ValueError(\"source.playbook_slug is required\")\n        playbook = get_playbook(playbook_slug)\n        variables = source.get(\"variables\") or {}\n        if not isinstance(variables, Mapping):\n            raise ValueError(\"source.variables must be an object\")\n        prompt = playbook.render(variables)\n        config[\"playbook\"] = playbook_slug\n    else:\n        raise ValueError(\"source.kind must be 'prompt' or 'playbook'\")\n\n    expression = str(schedule_spec.get(\"expression\") or \"\").strip()\n    timezone = schedule_spec.get(\"timezone\")\n    validate_schedule(expression)\n    if is_interval_schedule(expression):\n        validate_timezone_shape(timezone)\n    else:\n        validate_timezone(timezone)\n    next_run_at = now\n    if timezone is not None and not is_interval_schedule(expression):\n        next_run_at = next_due(expression, now, timezone)\n\n    end_at = _parse_end_at(draft.get(\"end_at\"))\n    if end_at is not None and end_at <= now:\n        raise ValueError(\"end_at must be in the future\")\n    if end_at is not None and next_run_at > end_at:\n        raise ValueError(\"the first scheduled run occurs after end_at\")\n","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/service.py#L132-L168","documentation":"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.","triggerScenarios":"source={\"kind\":\"workflow\"}; typo like 'promt' or 'Playbook' (case-sensitive); passing a kind value from a newer/older schema.","commonSituations":"Client/server version drift adding new source kinds; user-typed kind values; case mismatches from locale-aware lowercasing.","solutions":["Use 'prompt' or 'playbook' exactly (lowercase)","Omit kind entirely — it defaults to 'prompt'","Align client and server on the supported kind enum after upgrades"],"exampleFix":"// before\n{\"source\": {\"kind\": \"Playbook\", \"playbook_slug\": \"x\"}}\n// after\n{\"source\": {\"kind\": \"playbook\", \"playbook_slug\": \"x\"}}","handlingStrategy":"validation","validationCode":"assert draft.get(\"source\", {}).get(\"kind\", \"prompt\") in {\"prompt\", \"playbook\"}","typeGuard":"def source_kind_valid(src: dict) -> bool:\n    return str(src.get(\"kind\") or \"prompt\") in {\"prompt\", \"playbook\"}","tryCatchPattern":"try:\n    propose_create(draft)\nexcept ValueError as e:\n    if \"source.kind\" in str(e):\n        draft[\"source\"][\"kind\"] = \"prompt\"  # normalize fallback","preventionTips":["Expose kind as a closed enum in UI","Lowercase/trim kind values client-side before submit"],"tags":["scheduled-research","validation","enum"],"backgroundTag":"unsupported-operation-value","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}