{"record":{"id":"a3d164fbd141999f","repo":"HKUDS/Vibe-Trading","slug":"title-must-be-a-string","errorCode":null,"errorMessage":"'title' must be a string","messagePattern":"'title' must be a string","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"agent/src/scheduled_research/models.py","lineNumber":478,"sourceCode":"        # one job to UTC — the semantics it had before the field existed —\n        # instead of taking every other job down with it. Absent, blank, and\n        # non-string values all normalize to None.\n        raw_tz = data.get(\"timezone\")\n        tz = raw_tz if isinstance(raw_tz, str) and raw_tz.strip() else None\n        if raw_tz is not None and tz is None:\n            logger.warning(\n                \"scheduled research job %s has an unusable timezone %r; \"\n                \"evaluating its schedule in UTC\",\n                job_id,\n                raw_tz,\n            )\n        status = JobStatus(data[\"status\"])\n        title = data.get(\"title\", \"\")\n        source_type = data.get(\"source_type\", \"prompt\")\n        playbook_slug = data.get(\"playbook_slug\")\n        end_at = data.get(\"end_at\")\n        if not isinstance(title, str):\n            raise TypeError(\"'title' must be a string\")\n        if source_type not in {\"prompt\", \"playbook\"}:\n            raise ValueError(\"'source_type' must be 'prompt' or 'playbook'\")\n        if playbook_slug is not None and not isinstance(playbook_slug, str):\n            raise TypeError(\"'playbook_slug' must be a string or null\")\n        if end_at is not None and (isinstance(end_at, bool) or not isinstance(end_at, int)):\n            raise TypeError(\"'end_at' must be an integer (epoch ms) or null\")\n        raw_config = data.get(\"config\")\n        config: Dict[str, Any] = raw_config if isinstance(raw_config, dict) else {}\n        delivery_channel = data.get(\"delivery_channel\")\n        delivery_target = data.get(\"delivery_target\")\n        delivery_target_ref = data.get(\"delivery_target_ref\")\n        delivery_target_label = data.get(\"delivery_target_label\")\n        for name, value in (\n            (\"delivery_channel\", delivery_channel),\n            (\"delivery_target\", delivery_target),\n            (\"delivery_target_ref\", delivery_target_ref),\n            (\"delivery_target_label\", delivery_target_label),\n        ):","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/models.py#L460-L496","documentation":"Raised by Job from_dict (agent/src/scheduled_research/models.py:478) when the optional 'title' field (default '') is present but is not a string. Titles are display metadata; any non-str value such as a number or list fails. The default when absent is the empty string, which passes.","triggerScenarios":"{\"title\": 123} or {\"title\": [\"Morning\", \"Digest\"]}; absent key is fine because of the '' default.","commonSituations":"Titles derived from user numeric input or template lists; API layers that pass through untyped JSON bodies.","solutions":["Coerce to str: str(title) when building the record","Validate user-supplied titles as strings at your API boundary","Omit the field if no title"],"exampleFix":"// before\nrecord[\"title\"] = run_count\n\n// after\nrecord[\"title\"] = f\"Run #{run_count}\"","handlingStrategy":"validation","validationCode":"def title_ok(d):\n    v = d.get(\"title\", \"\")\n    return isinstance(v, str)","typeGuard":"def safe_title(v, default=\"\"):\n    return v if isinstance(v, str) else default","tryCatchPattern":"try:\n    job = Job.from_dict(record)\nexcept TypeError as exc:\n    if \"title\" in str(exc):\n        record = dict(record); record[\"title\"] = str(record.get(\"title\", \"\"))\n        job = Job.from_dict(record)\n    else:\n        raise","preventionTips":["Validate title as str at the API boundary","Default to '' when generating records programmatically","F-string any interpolated numbers into titles"],"tags":["deserialization","type-error","scheduled-research"],"backgroundTag":"json-field-type-mismatch","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}