HKUDS/Vibe-Trading · error · TypeError
'delivery' must be an object or null
Error message
'delivery' must be an object or null
What it means
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.
Source
Thrown at agent/src/scheduled_research/models.py:274
@classmethod
def from_dict(cls, data: Optional[Dict[str, Any]]) -> "DeliveryRecord":
"""Reconstruct from a raw dict, treating absence as "never delivered".
Args:
data: A raw dict as produced by :meth:`to_dict`, or ``None`` for a
record written before delivery existed.
Returns:
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"),View on GitHub (pinned to 80ffdda44c)
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
Example fix
// before
{"delivery": "sent"}
// after
{"delivery": {"status": "sent"}} Defensive patterns
Strategy: type-guard
Validate before calling
def valid_delivery(data):
return data is None or isinstance(data, dict) Type guard
from typing import Any, Optional, Dict
def as_delivery_dict(data: Any) -> Optional[Dict]:
if data is None:
return None
if isinstance(data, dict):
return data
if isinstance(data, str):
import json
try:
parsed = json.loads(data)
return parsed if isinstance(parsed, dict) else None
except json.JSONDecodeError:
return None
return None Try / catch
try:
delivery = DeliveryInfo.from_dict(raw)
except TypeError as exc:
if "'delivery' must be an object" in str(exc):
delivery = DeliveryInfo() # reset to defaults
else:
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- 'delivery.{name}' must be a string 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/f2b3e8bf03eab928.
Report an issue: GitHub.