{"record":{"id":"ba4bc0f194e69a44","repo":"HKUDS/Vibe-Trading","slug":"cron-schedule-has-no-matching-time-within-search-w","errorCode":null,"errorMessage":"cron schedule has no matching time within search window: {schedule!r}","messagePattern":"cron schedule has no matching time within search window: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/scheduled_research/executor.py","lineNumber":161,"sourceCode":"    minutes, hours, doms, months, dows = (\n        parse_cron_field(part, low, high) for part, (low, high) in zip(schedule.split(), CRON_BOUNDS)\n    )\n    zone = timezone.utc if tz is None else ZoneInfo(tz)\n    # Walk candidates on the *local* calendar of ``zone`` so field matching —\n    # the weekday in particular — follows the authoring wall clock. Ascending\n    # wall order maps to ascending UTC order under a fixed fold policy, so\n    # \"strictly after\" stays a plain epoch comparison.\n    day = datetime.fromtimestamp(after_ms / 1000.0, zone).date()\n    for offset in range(_CRON_SEARCH_LIMIT_DAYS):\n        candidate_day = day + timedelta(days=offset)\n        if not _day_matches(candidate_day, doms, months, dows):\n            continue\n        for hour in sorted(hours) if hours is not None else range(24):\n            for minute in sorted(minutes) if minutes is not None else range(60):\n                fire_ms = _local_wall_time_to_epoch_ms(candidate_day, hour, minute, zone)\n                if fire_ms is not None and fire_ms > after_ms:\n                    return fire_ms\n    raise ValueError(f\"cron schedule has no matching time within search window: {schedule!r}\")\n\n\ndef _local_wall_time_to_epoch_ms(day: date, hour: int, minute: int, zone: tzinfo) -> int | None:\n    \"\"\"Resolve one local wall time to a UTC epoch-ms instant.\n\n    DST policy (#953): a nonexistent wall time — the spring-forward gap —\n    returns ``None`` so the occurrence is skipped; ``ZoneInfo`` would\n    otherwise silently map it past the transition (PEP 495) and run it. An\n    ambiguous wall time — the fall-back fold — resolves with ``fold=0``, the\n    first occurrence, so it runs exactly once.\n    \"\"\"\n    local = datetime(day.year, day.month, day.day, hour, minute, tzinfo=zone)\n    as_utc = local.astimezone(timezone.utc)\n    if as_utc.astimezone(zone).replace(tzinfo=None) != local.replace(tzinfo=None):\n        return None  # wall time does not exist in this zone (DST gap)\n    return int(as_utc.timestamp() * 1000)\n\n","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/executor.py#L143-L179","documentation":"_next_cron_due scans forward through days (and each matching hour/minute in the schedule's timezone) to find the next fire time after a given instant. If the search window contains no resolvable instant — e.g. every candidate wall time falls into a DST spring-forward gap and is skipped — the function gives up with this error instead of looping forever.","triggerScenarios":"A cron schedule whose only fire times are wall times that never exist (e.g. '0 2 * * *' in a timezone where 02:00 is always inside the spring-forward gap on relevant days), or pathological schedules combined with an after_ms far in the future beyond the scan window.","commonSituations":"Timezones like America/Sao_Paulo or historical zones whose DST transitions skip the scheduled hour; custom or misconfigured tz databases on the host; a schedule generated programmatically that pins an impossible hour.","solutions":["Pick a fire time that exists year-round in the job's timezone (avoid 02:00-03:00 style hours near common DST shifts).","Run jobs in UTC unless local-time firing is required.","If the hour must be local, schedule a minute that is not skipped (e.g. 02:45 often survives a 1-hour gap shift)."],"exampleFix":"# before\nschedule = '0 2 * * *'; tz = 'America/Sao_Paulo'\n# after\nschedule = '0 4 * * *'; tz = 'America/Sao_Paulo'","handlingStrategy":"validation","validationCode":"from scheduled_research.models import validate_schedule\nvalidate_schedule(schedule)\n# avoid fire hours inside common DST gaps\nassert not (schedule.startswith('0 2 ') or schedule.startswith('0 30 2'))","typeGuard":null,"tryCatchPattern":"try:\n    due = job.next_due(after_ms)\nexcept ValueError as e:\n    if 'no matching time' in str(e):\n        mark_job_schedule_failed(job)  # per-job failure, not a crash","preventionTips":["Avoid scheduling at 02:00-03:00 local time.","Prefer UTC for machine-driven jobs.","Smoke-test every cron+tz pair with next_due at job creation."],"tags":["python","cron","scheduler","dst","timezone"],"backgroundTag":"cron-schedule-unresolvable","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}