HKUDS/Vibe-Trading · error · TypeError
'delivery.{name}' must be a string or null
Error message
'delivery.{name}' must be a string or null What it means
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.
Source
Thrown at agent/src/scheduled_research/models.py:283
The reconstructed :class:`DeliveryRecord`.
Raises:
TypeError: If a present field has the wrong type.
ValueError: If ``status`` is not a recognized value.
"""
if not data:
return cls()
if not isinstance(data, dict):
raise TypeError("'delivery' must be an object or null")
raw_status = data.get("status", DeliveryStatus.NONE.value)
try:
status = DeliveryStatus(raw_status)
except ValueError as exc:
raise ValueError(f"unknown delivery status {raw_status!r}") from exc
for name in ("session_id", "key", "error", "provider_message_id"):
value = data.get(name)
if value is not None and not isinstance(value, str):
raise TypeError(f"'delivery.{name}' must be a string or null")
updated_at = data.get("updated_at")
if updated_at is not None and not isinstance(updated_at, int):
raise TypeError("'delivery.updated_at' must be an integer (epoch ms) or null")
attempts = data.get("attempts", 0)
if isinstance(attempts, bool) or not isinstance(attempts, int) or attempts < 0:
raise TypeError("'delivery.attempts' must be a non-negative integer")
return cls(
status=status,
session_id=data.get("session_id"),
key=data.get("key"),
error=data.get("error"),
attempts=attempts,
updated_at=updated_at,
provider_message_id=data.get("provider_message_id"),
)
# ---------------------------------------------------------------------------View on GitHub (pinned to 80ffdda44c)
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
Example fix
// before
delivery = {"session_id": 12345, "status": "sent"}
// after
delivery = {"session_id": str(12345), "status": "sent"} Defensive patterns
Strategy: validation
Validate before calling
STR_FIELDS = ("session_id", "key", "error", "provider_message_id")
def delivery_strings_ok(d):
return d is None or isinstance(d, dict) and all(
d.get(k) is None or isinstance(d.get(k), str) for k in STR_FIELDS
) Type guard
from typing import Any, Dict
def sanitized_delivery(data: Dict[str, Any]) -> Dict[str, Any]:
out = dict(data)
for k in ("session_id", "key", "error", "provider_message_id"):
v = out.get(k)
if v is not None and not isinstance(v, str):
out[k] = str(v)
return out Try / catch
try:
delivery = DeliveryInfo.from_dict(raw)
except TypeError:
delivery = DeliveryInfo() # or log the raw record and quarantine it Prevention
- 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
When it happens
Trigger: delivery = {"session_id": 12345} (numeric ID from a DB), {"key": ["a"]}, or {"error": {"msg": "x"}}. Omitted keys default to None and are fine.
Common situations: 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.
Related errors
- 'delivery' must be an object or null
- 'delivery.attempts' must be a non-negative integer
- 'id', 'prompt', and 'schedule' must be strings
- 'consecutive_failures' must be a non-negative integer
- 'last_error' must be a string or null
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/616a9a2fada5d140.
Report an issue: GitHub.