{"record":{"id":"616a9a2fada5d140","repo":"HKUDS/Vibe-Trading","slug":"delivery-name-must-be-a-string-or-null","errorCode":null,"errorMessage":"'delivery.{name}' must be a string or null","messagePattern":"'delivery\\.(.+?)' must be a string or null","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"agent/src/scheduled_research/models.py","lineNumber":283,"sourceCode":"            The reconstructed :class:`DeliveryRecord`.\n\n        Raises:\n            TypeError: If a present field has the wrong type.\n            ValueError: If ``status`` is not a recognized value.\n        \"\"\"\n        if not data:\n            return cls()\n        if not isinstance(data, dict):\n            raise TypeError(\"'delivery' must be an object or null\")\n        raw_status = data.get(\"status\", DeliveryStatus.NONE.value)\n        try:\n            status = DeliveryStatus(raw_status)\n        except ValueError as exc:\n            raise ValueError(f\"unknown delivery status {raw_status!r}\") from exc\n        for name in (\"session_id\", \"key\", \"error\", \"provider_message_id\"):\n            value = data.get(name)\n            if value is not None and not isinstance(value, str):\n                raise TypeError(f\"'delivery.{name}' must be a string or null\")\n        updated_at = data.get(\"updated_at\")\n        if updated_at is not None and not isinstance(updated_at, int):\n            raise TypeError(\"'delivery.updated_at' must be an integer (epoch ms) or null\")\n        attempts = data.get(\"attempts\", 0)\n        if isinstance(attempts, bool) or not isinstance(attempts, int) or attempts < 0:\n            raise TypeError(\"'delivery.attempts' must be a non-negative integer\")\n        return cls(\n            status=status,\n            session_id=data.get(\"session_id\"),\n            key=data.get(\"key\"),\n            error=data.get(\"error\"),\n            attempts=attempts,\n            updated_at=updated_at,\n            provider_message_id=data.get(\"provider_message_id\"),\n        )\n\n\n# ---------------------------------------------------------------------------","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/models.py#L265-L301","documentation":"Raised by delivery from_dict (agent/src/scheduled_research/models.py:283) when one of the string fields session_id, key, error, or provider_message_id inside the serialized 'delivery' object is present but not a string and not null. The deserializer enforces these fields are str|null since they are identifiers and human-readable error text.","triggerScenarios":"delivery = {\"session_id\": 12345} (numeric ID from a DB), {\"key\": [\"a\"]}, or {\"error\": {\"msg\": \"x\"}}. Omitted keys default to None and are fine.","commonSituations":"Coercing IDs to integers when building the payload; nested error objects instead of a formatted message string; schema drift after changing what these fields hold.","solutions":["Coerce identifiers to str before persisting: str(session_id)","Flatten error info into a single string message","Omit fields you don't have rather than sending empty dicts/lists"],"exampleFix":"// before\ndelivery = {\"session_id\": 12345, \"status\": \"sent\"}\n\n// after\ndelivery = {\"session_id\": str(12345), \"status\": \"sent\"}","handlingStrategy":"validation","validationCode":"STR_FIELDS = (\"session_id\", \"key\", \"error\", \"provider_message_id\")\n\ndef delivery_strings_ok(d):\n    return d is None or isinstance(d, dict) and all(\n        d.get(k) is None or isinstance(d.get(k), str) for k in STR_FIELDS\n    )","typeGuard":"from typing import Any, Dict\n\ndef sanitized_delivery(data: Dict[str, Any]) -> Dict[str, Any]:\n    out = dict(data)\n    for k in (\"session_id\", \"key\", \"error\", \"provider_message_id\"):\n        v = out.get(k)\n        if v is not None and not isinstance(v, str):\n            out[k] = str(v)\n    return out","tryCatchPattern":"try:\n    delivery = DeliveryInfo.from_dict(raw)\nexcept TypeError:\n    delivery = DeliveryInfo()  # or log the raw record and quarantine it","preventionTips":["Cast all IDs to str when building delivery payloads","Keep error fields as single formatted strings","Generate the dict from the dataclass instead of assembling it by hand"],"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"}