{"record":{"id":"ddfe284cf23a64de","repo":"HKUDS/Vibe-Trading","slug":"delivery-updated-at-must-be-an-integer-epoch-ms","errorCode":null,"errorMessage":"'delivery.updated_at' must be an integer (epoch ms) or null","messagePattern":"'delivery\\.updated_at' must be an integer \\(epoch ms\\) or null","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"agent/src/scheduled_research/models.py","lineNumber":286,"sourceCode":"            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# ---------------------------------------------------------------------------\n# Data model\n# ---------------------------------------------------------------------------\n","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/models.py#L268-L304","documentation":"Raised by delivery from_dict (agent/src/scheduled_research/models.py:286) when the optional 'updated_at' field in the delivery object is present but is not an integer. Timestamps are stored as epoch milliseconds (ints); float seconds, ISO strings, or Date objects fail this check. null is accepted.","triggerScenarios":"delivery = {\"updated_at\": \"2026-01-01T00:00:00Z\"} or {\"updated_at\": 1767225600.5} or {\"updated_at\": 1767225600000/n} where n makes it a float.","commonSituations":"Writing epoch seconds instead of milliseconds; serializing datetime objects as ISO strings via a generic JSON encoder; floats creeping in from arithmetic on timestamps.","solutions":["Convert to epoch ms int: int(dt.timestamp() * 1000)","Round/convert floats: int(round(value))","Serialize datetimes explicitly to int ms rather than relying on default JSON encoders","Omit the field (null) if you don't track it"],"exampleFix":"// before\ndelivery = {\"updated_at\": datetime.now(timezone.utc).isoformat()}\n\n// after\nfrom datetime import datetime, timezone\ndelivery = {\"updated_at\": int(datetime.now(timezone.utc).timestamp() * 1000)}","handlingStrategy":"validation","validationCode":"from datetime import datetime, timezone\n\ndef to_epoch_ms(dt):\n    return int(dt.timestamp() * 1000) if dt else None\n\ndef updated_at_ok(v):\n    return v is None or (isinstance(v, int) and not isinstance(v, bool))","typeGuard":"from typing import Any, Optional\n\ndef as_epoch_ms(value: Any) -> Optional[int]:\n    if value is None:\n        return None\n    if isinstance(value, bool):\n        return None\n    if isinstance(value, (int, float)):\n        return int(value)\n    if isinstance(value, str):\n        try:\n            return int(datetime.fromisoformat(value.replace(\"Z\", \"+00:00\")).timestamp() * 1000)\n        except ValueError:\n            return None\n    return None","tryCatchPattern":"try:\n    delivery = DeliveryInfo.from_dict(raw)\nexcept TypeError as exc:\n    if \"updated_at\" in str(exc):\n        raw = dict(raw); raw[\"updated_at\"] = None\n        delivery = DeliveryInfo.from_dict(raw)\n    else:\n        raise","preventionTips":["Use one to_epoch_ms helper for every persisted timestamp","Never rely on default JSON encoders for datetime fields","Decide seconds vs milliseconds once and stick to milliseconds"],"tags":["deserialization","timestamp","epoch-milliseconds","scheduled-research"],"backgroundTag":"timestamp-format-mismatch","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}