HKUDS/Vibe-Trading · error · TypeError
'next_run_at' and 'created_at' must be integers (epoch ms)
Error message
'next_run_at' and 'created_at' must be integers (epoch ms)
What it means
Raised by Job from_dict (agent/src/scheduled_research/models.py:441) when the required 'next_run_at' or 'created_at' fields are not integers. Both are epoch-millisecond timestamps and must be int; floats, numeric strings, or ISO date strings fail. Keys are fetched with data[...], so absence raises KeyError first.
Source
Thrown at agent/src/scheduled_research/models.py:441
data: A raw dict as produced by :meth:`to_dict`.
Returns:
The reconstructed ``ScheduledResearchJob``.
Raises:
KeyError: If a required field is missing.
TypeError: If a field has the wrong type.
ValueError: If ``status`` is not a recognized ``JobStatus`` value.
"""
job_id = data["id"]
prompt = data["prompt"]
schedule = data["schedule"]
if not isinstance(job_id, str) or not isinstance(prompt, str) or not isinstance(schedule, str):
raise TypeError("'id', 'prompt', and 'schedule' must be strings")
next_run_at = data["next_run_at"]
created_at = data["created_at"]
if not isinstance(next_run_at, int) or not isinstance(created_at, int):
raise TypeError("'next_run_at' and 'created_at' must be integers (epoch ms)")
last_run_at = data.get("last_run_at")
if last_run_at is not None and not isinstance(last_run_at, int):
raise TypeError("'last_run_at' must be an integer (epoch ms) or null")
consecutive_failures = data.get("consecutive_failures", 0)
if (
isinstance(consecutive_failures, bool)
or not isinstance(consecutive_failures, int)
or consecutive_failures < 0
):
raise TypeError("'consecutive_failures' must be a non-negative integer")
last_error = data.get("last_error")
failure_kind = data.get("failure_kind")
if last_error is not None and not isinstance(last_error, str):
raise TypeError("'last_error' must be a string or null")
if failure_kind is not None and failure_kind not in {"dispatch", "schedule"}:
raise ValueError("'failure_kind' must be 'dispatch', 'schedule', or null")
# Never raises: the store quarantines the whole file when a single
# record fails to load, so an unusable timezone value degrades thatView on GitHub (pinned to 80ffdda44c)
Solutions
- Write epoch ms ints: int(dt.timestamp() * 1000)
- Convert numeric strings: int(value)
- Standardize on milliseconds everywhere in your persistence layer
Example fix
// before
{"created_at": "2026-01-01T00:00:00Z", "next_run_at": "1767225600000"}
// after
{"created_at": 1767225600000, "next_run_at": 1767312000000} Defensive patterns
Strategy: validation
Validate before calling
def epoch_int_ok(d):
return all(isinstance(d.get(k), int) and not isinstance(d.get(k), bool)
for k in ("next_run_at", "created_at")) Type guard
from typing import Any, Dict
def with_epoch_ints(data: Dict[str, Any]) -> Dict[str, Any]:
out = dict(data)
for k in ("next_run_at", "created_at"):
v = out.get(k)
if isinstance(v, bool):
raise TypeError(f"{k} cannot be bool")
if isinstance(v, str):
out[k] = int(v)
elif isinstance(v, float):
out[k] = int(v)
return out Try / catch
try:
job = Job.from_dict(record)
except TypeError as exc:
if "epoch ms" in str(exc):
record = {**record, "created_at": int(record["created_at"]),
"next_run_at": int(record["next_run_at"])}
job = Job.from_dict(record)
else:
raise Prevention
- Single epoch-ms helper for all timestamps
- Reject string timestamps at ingestion
- Use ints, not floats, in all persisted timestamp math
When it happens
Trigger: {"created_at": "1767225600000"}, {"next_run_at": 1767225600.0}, or writing datetime.isoformat() into either field.
Common situations: Epoch seconds vs milliseconds confusion; string-encoded numbers from JSON produced by another language or query params; ISO strings from a default datetime JSON encoder.
Related errors
- 'delivery.updated_at' must be an integer (epoch ms) or null
- 'last_run_at' must be an integer (epoch ms) or null
- 'end_at' must be an integer (epoch ms) or null
- 'delivery' must be an object or null
- unknown delivery status {raw_status!r}
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/05f897431212d5c9.
Report an issue: GitHub.