{"record":{"id":"38ef0e205f1067f6","repo":"HKUDS/Vibe-Trading","slug":"title-is-required-38ef0e","errorCode":null,"errorMessage":"title is required","messagePattern":"title is required","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"agent/src/scheduled_research/service.py","lineNumber":123,"sourceCode":"    config = getattr(session, \"config\", None) or {}\n    channel = config.get(\"channel\")\n    target = config.get(\"channel_chat_id\")\n    if not isinstance(channel, str) or not isinstance(target, str):\n        raise ValueError(\"the originating session is not an IM conversation\")\n    return channel, target, None, \"当前会话\"\n\n\ndef build_job_from_draft(\n    draft: Mapping[str, Any],\n    *,\n    session_id: str | None = None,\n    now_ms: int | None = None,\n) -> ScheduledResearchJob:\n    \"\"\"Validate the public draft and build a persisted-model job.\"\"\"\n    now = int(time.time() * 1000) if now_ms is None else now_ms\n    title = str(draft.get(\"title\") or \"\").strip()\n    if not title:\n        raise ValueError(\"title is required\")\n    source = draft.get(\"source\")\n    schedule_spec = draft.get(\"schedule\")\n    delivery_spec = draft.get(\"delivery\") or {\"mode\": \"in_app\"}\n    if not isinstance(source, Mapping) or not isinstance(schedule_spec, Mapping):\n        raise ValueError(\"source and schedule must be objects\")\n    if not isinstance(delivery_spec, Mapping):\n        raise ValueError(\"delivery must be an object\")\n\n    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:","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/service.py#L105-L141","documentation":"build_job_from_draft requires a non-empty draft['title'] after stripping whitespace. The title identifies the scheduled research job in listings and notifications, so it cannot be blank.","triggerScenarios":"Omitting 'title' from the draft JSON, sending \"\" or \"   \", or a form field that never got bound.","commonSituations":"UI forms where title is optional client-side but required server-side; JSON payloads built from templates with an unfilled title placeholder; whitespace-only input from copy-paste.","solutions":["Provide a non-empty title in the draft","Trim client-side and validate before submitting","Make the title field required in the form schema"],"exampleFix":"// before\n{\"title\": \"  \"}\n// after\n{\"title\": \"Daily competitor digest\"}","handlingStrategy":"validation","validationCode":"assert str(draft.get(\"title\") or \"\").strip(), \"title is required\"","typeGuard":"def has_title(draft: dict) -> bool:\n    return bool(str(draft.get(\"title\") or \"\").strip())","tryCatchPattern":"try:\n    propose_create(draft)\nexcept ValueError as e:\n    mark_required_field(\"title\") if \"title\" in str(e) else raise_","preventionTips":["Make title mandatory in UI forms","Validate required fields before the API round-trip"],"tags":["scheduled-research","validation","required-field"],"backgroundTag":"missing-required-field","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}