HKUDS/Vibe-Trading · error · ValueError
the first scheduled run occurs after end_at
Error message
the first scheduled run occurs after end_at
What it means
The job's first scheduled run (next_run_at, computed from the schedule expression/timezone, or 'now' for interval schedules) falls after end_at, so the job could never fire before expiring.
Source
Thrown at agent/src/scheduled_research/service.py:167
else:
raise ValueError("source.kind must be 'prompt' or 'playbook'")
expression = str(schedule_spec.get("expression") or "").strip()
timezone = schedule_spec.get("timezone")
validate_schedule(expression)
if is_interval_schedule(expression):
validate_timezone_shape(timezone)
else:
validate_timezone(timezone)
next_run_at = now
if timezone is not None and not is_interval_schedule(expression):
next_run_at = next_due(expression, now, timezone)
end_at = _parse_end_at(draft.get("end_at"))
if end_at is not None and end_at <= now:
raise ValueError("end_at must be in the future")
if end_at is not None and next_run_at > end_at:
raise ValueError("the first scheduled run occurs after end_at")
mode = str(delivery_spec.get("mode") or "in_app")
delivery_channel = delivery_target = target_ref = target_label = None
if mode == "configured":
target_ref = str(delivery_spec.get("target_ref") or "").strip()
if not target_ref:
raise ValueError("delivery.target_ref is required for configured delivery")
target = resolve_delivery_target(target_ref)
delivery_channel, delivery_target = target.channel, target.target
target_label = target.label
elif mode == "origin":
delivery_channel, delivery_target, target_ref, target_label = _origin_target(
session_id
)
elif mode != "in_app":
raise ValueError("delivery.mode must be 'in_app', 'origin', or 'configured'")
return ScheduledResearchJob(View on GitHub (pinned to 80ffdda44c)
Solutions
- Extend end_at beyond the first occurrence, or omit it
- Use a more frequent schedule expression so a run occurs before end_at
- Double-check timezone: compute next_due in the job's timezone to verify it precedes end_at
Example fix
// before
{"schedule": {"expression": "0 9 * * 1"}, "end_at": "<3 days from now>"}
// after
{"schedule": {"expression": "0 9 * * 1"}, "end_at": "<10 days from now>"} Defensive patterns
Strategy: validation
Validate before calling
from scheduled_research.schedule import next_due, is_interval_schedule nra = now_ms if is_interval_schedule(expr) else next_due(expr, now_ms, tz) assert end_at is None or nra <= end_at, "first run after end_at"
Type guard
def schedule_fits_window(expr: str, tz, end_at: int | None, now: int) -> bool:
return end_at is None or (now if is_interval_schedule(expr) else next_due(expr, now, tz)) <= end_at Try / catch
try:
propose_create(draft)
except ValueError as e:
if "after end_at" in str(e):
draft.pop("end_at", None); retry() # or extend end_at Prevention
- Pre-compute the first occurrence and compare with end_at client-side
- Avoid tight end windows on infrequent cron expressions
When it happens
Trigger: A weekly cron with an end_at 3 days out; interval schedules where next_run_at defaults to now but end_at is in the past millisecond boundary; timezone shifts pushing the next due date beyond end_at.
Common situations: Users setting short end dates on infrequent schedules; timezone conversions moving next_due across the end boundary; test fixtures with tiny end_at windows.
Related errors
- end_at must be RFC3339 text or epoch milliseconds
- end_at must be in the future
- 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/e033f1b7ff193f8f.
Report an issue: GitHub.