{"record":{"id":"54cc8dfc3330b354","repo":"HKUDS/Vibe-Trading","slug":"playbook-slug-must-be-a-string-or-null","errorCode":null,"errorMessage":"'playbook_slug' must be a string or null","messagePattern":"'playbook_slug' must be a string or null","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"agent/src/scheduled_research/models.py","lineNumber":482,"sourceCode":"        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        ):\n            if value is not None and not isinstance(value, str):\n                raise TypeError(f\"'{name}' must be a string or null\")\n        return cls(\n            id=job_id,","sourceCodeStart":464,"sourceCodeEnd":500,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/models.py#L464-L500","documentation":"Raised by Job from_dict (agent/src/scheduled_research/models.py:482) when 'playbook_slug' is present but is neither a string nor null. The slug references a playbook file for playbook-sourced jobs; non-string values (int, list, dict) are rejected. null is valid and expected for prompt-sourced jobs.","triggerScenarios":"{\"playbook_slug\": 42} or {\"playbook_slug\": [\"daily\"]} on a playbook job record; absent or null passes.","commonSituations":"Slugs stored as identifiers with numeric prefixes coerced to int; building records from untyped API payloads.","solutions":["Keep slugs as strings: str(slug)","Omit or null the field for prompt-sourced jobs","Validate slug format at your input boundary"],"exampleFix":"// before\nrecord[\"playbook_slug\"] = 42\n\n// after\nrecord[\"playbook_slug\"] = \"42\"  # or the actual slug string","handlingStrategy":"validation","validationCode":"def playbook_slug_ok(d):\n    v = d.get(\"playbook_slug\")\n    return v is None or isinstance(v, str)","typeGuard":"def safe_slug(v):\n    return v if (v is None or isinstance(v, str)) else str(v)","tryCatchPattern":"try:\n    job = Job.from_dict(record)\nexcept TypeError as exc:\n    if \"playbook_slug\" in str(exc):\n        record = dict(record); record[\"playbook_slug\"] = None\n        job = Job.from_dict(record)\n    else:\n        raise","preventionTips":["Keep slugs strings end to end","Null the field for prompt-sourced jobs","Validate slug format (regex) at input"],"tags":["deserialization","type-error","playbook","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"}