HKUDS/Vibe-Trading · error · ValueError
end_at must be RFC3339 text or epoch milliseconds
Error message
end_at must be RFC3339 text or epoch milliseconds
What it means
_parse_end_at rejects the end_at draft field when it is a boolean. Since bool is a subclass of int in Python, the explicit check exists to stop `true`/`false` JSON values from being silently treated as epoch 1/0 milliseconds.
Source
Thrown at agent/src/scheduled_research/service.py:81
host = sys.modules.get("api_server") or sys.modules.get("agent.api_server")
executor = getattr(host, "_scheduled_research_executor", None) if host else None
if executor is None:
try:
from src.api import scheduled_routes
executor = scheduled_routes._scheduled_research_executor
except Exception:
executor = None
if executor is not None:
running = bool(executor.is_running)
return {"enabled": enabled, "running": running, "executable": enabled and running}
def _parse_end_at(value: Any) -> int | None:
if value in (None, ""):
return None
if isinstance(value, bool):
raise ValueError("end_at must be RFC3339 text or epoch milliseconds")
if isinstance(value, int):
return value
if not isinstance(value, str):
raise ValueError("end_at must be RFC3339 text or epoch milliseconds")
try:
parsed = datetime.fromisoformat(value.strip().replace("Z", "+00:00"))
except ValueError as exc:
raise ValueError("end_at must be a valid RFC3339 timestamp") from exc
if parsed.tzinfo is None:
raise ValueError("end_at must include an explicit timezone offset")
return int(parsed.timestamp() * 1000)
def _origin_target(session_id: str | None) -> tuple[str, str, str | None, str]:
if not session_id:
raise ValueError("origin delivery requires the originating session")
host = sys.modules.get("api_server") or sys.modules.get("agent.api_server")
service = (View on GitHub (pinned to 80ffdda44c)
Solutions
- Send end_at as null/omitted for no end date, an RFC3339 string, or epoch milliseconds int
- Audit the client code that serializes the draft for boolean coercion
- If using YAML, quote values like "true" so they stay strings, or better, use real timestamps
Example fix
// before
{"end_at": true}
// after
{"end_at": null} // or "2026-12-31T00:00:00Z" or 1798761600000 Defensive patterns
Strategy: validation
Validate before calling
assert draft.get("end_at") is None or isinstance(draft.get("end_at"), (str, int)) and not isinstance(draft.get("end_at"), bool) Type guard
def valid_end_at_type(v) -> bool:
return v is None or v == "" or (isinstance(v, (int, str)) and not isinstance(v, bool)) Try / catch
try:
build_job_from_draft(draft)
except ValueError as e:
if "RFC3339 text or epoch milliseconds" in str(e):
fix_end_at_field() Prevention
- Never bind booleans to end_at in forms
- Schema-validate drafts client-side (string|int|null only)
When it happens
Trigger: Passing end_at: true or end_at: false in the draft JSON (e.g. from a checkbox or feature flag leaking into the payload); any JSON boolean where a timestamp is expected.
Common situations: Frontend forms binding a toggle/checkbox to the end_at field; YAML/JSON config where an unquoted yes/no becomes a boolean; defaulting end_at to a flag instead of null.
Related errors
- end_at must be in the future
- the first scheduled run occurs after end_at
- timezone {tz!r} is not a recognized IANA timezone key
- unknown delivery status {raw_status!r}
- 'failure_kind' must be 'dispatch', 'schedule', or null
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/1811e7f06420df8f.
Report an issue: GitHub.