{"record":{"id":"d782ae66c3697be1","repo":"HKUDS/Vibe-Trading","slug":"delivery-attempts-must-be-a-non-negative-integer","errorCode":null,"errorMessage":"'delivery.attempts' must be a non-negative integer","messagePattern":"'delivery\\.attempts' must be a non-negative integer","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"agent/src/scheduled_research/models.py","lineNumber":289,"sourceCode":"        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\n\ndef _verdict_record_or_none(data: Any, job_id: str) -> Optional[VerdictRecord]:\n    \"\"\"Parse a persisted last_verdict, degrading an unreadable one to None.","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/models.py#L271-L307","documentation":"Raised by delivery from_dict (agent/src/scheduled_research/models.py:289) when the 'attempts' field in the delivery object is a bool, a non-int, or a negative int. The bool check exists because isinstance(True, int) is True in Python, so explicit rejection is needed. Defaults to 0 when absent.","triggerScenarios":"delivery = {\"attempts\": -1}, {\"attempts\": \"3\"}, {\"attempts\": 2.0}, or {\"attempts\": True}. All four hit the combined isinstance/bool/negative guard.","commonSituations":"Decrementing attempts below zero in retry logic; JSON round-tripping ints as strings; flags accidentally stored in the attempts slot; floats after division-based backoff math.","solutions":["Clamp retry counters: max(0, int(attempts))","Keep attempts as int through all retry arithmetic","Omit the field to use the default of 0"],"exampleFix":"// before\ndelivery = {\"attempts\": attempts - 1}  # can reach -1\n\n// after\ndelivery = {\"attempts\": max(0, attempts - 1)}","handlingStrategy":"validation","validationCode":"def attempts_ok(v):\n    return not isinstance(v, bool) and isinstance(v, int) and v >= 0","typeGuard":"def safe_attempts(v, default=0):\n    if isinstance(v, bool) or not isinstance(v, int) or v < 0:\n        return default\n    return v","tryCatchPattern":"try:\n    delivery = DeliveryInfo.from_dict(raw)\nexcept TypeError as exc:\n    if \"attempts\" in str(exc):\n        raw = dict(raw); raw[\"attempts\"] = 0\n        delivery = DeliveryInfo.from_dict(raw)\n    else:\n        raise","preventionTips":["Clamp retry counters with max(0, n) on every decrement","Keep counters as ints; never store flags in numeric fields","Round-trip test your persisted records"],"tags":["deserialization","type-error","retry-counter","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"}