{"record":{"id":"f2b3e8bf03eab928","repo":"HKUDS/Vibe-Trading","slug":"delivery-must-be-an-object-or-null","errorCode":null,"errorMessage":"'delivery' must be an object or null","messagePattern":"'delivery' must be an object or null","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"agent/src/scheduled_research/models.py","lineNumber":274,"sourceCode":"    @classmethod\n    def from_dict(cls, data: Optional[Dict[str, Any]]) -> \"DeliveryRecord\":\n        \"\"\"Reconstruct from a raw dict, treating absence as \"never delivered\".\n\n        Args:\n            data: A raw dict as produced by :meth:`to_dict`, or ``None`` for a\n                record written before delivery existed.\n\n        Returns:\n            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\"),","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/models.py#L256-L292","documentation":"Raised by ScheduledRun delivery from_dict (agent/src/scheduled_research/models.py:274) when the serialized 'delivery' payload is present but is not a JSON object (dict). The deserializer expects delivery to be an object or null so it can read keys like status, session_id, and attempts. Any other JSON type (string, list, number, boolean) is a type error.","triggerScenarios":"Calling from_dict on a stored record where delivery was serialized as a JSON string like '\"none\"' or a list, e.g. {\"delivery\": \"pending\"} instead of {\"delivery\": {\"status\": \"pending\"}}. Empty/None data returns early and never raises.","commonSituations":"Hand-editing a persisted jobs.json; a migration script writing delivery as a flat string; double-encoding (json.dumps applied twice) producing a string payload; another tool writing the store with a different schema.","solutions":["Inspect the stored JSON and fix delivery to be an object: {\"status\": \"none\"} or null","Find the writer that produced the malformed value and serialize the delivery dict as-is","If hand-migrating, delete the delivery key to reset it to defaults","Add a pre-load schema check on the store file before from_dict"],"exampleFix":"// before\n{\"delivery\": \"sent\"}\n\n// after\n{\"delivery\": {\"status\": \"sent\"}}","handlingStrategy":"type-guard","validationCode":"def valid_delivery(data):\n    return data is None or isinstance(data, dict)","typeGuard":"from typing import Any, Optional, Dict\n\ndef as_delivery_dict(data: Any) -> Optional[Dict]:\n    if data is None:\n        return None\n    if isinstance(data, dict):\n        return data\n    if isinstance(data, str):\n        import json\n        try:\n            parsed = json.loads(data)\n            return parsed if isinstance(parsed, dict) else None\n        except json.JSONDecodeError:\n            return None\n    return None","tryCatchPattern":"try:\n    delivery = DeliveryInfo.from_dict(raw)\nexcept TypeError as exc:\n    if \"'delivery' must be an object\" in str(exc):\n        delivery = DeliveryInfo()  # reset to defaults\n    else:\n        raise","preventionTips":["Always serialize the delivery dataclass via to_dict, never hand-build it","json.dumps the whole record exactly once","Add a smoke-test that round-trips every persisted record through from_dict"],"tags":["deserialization","type-error","json","scheduled-research"],"backgroundTag":"json-schema-type-mismatch","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}