HKUDS/Vibe-Trading · error · ValueError

cron schedule has no matching time within search window: {sc

Error message

cron schedule has no matching time within search window: {schedule!r}

What it means

_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.

Source

Thrown at agent/src/scheduled_research/executor.py:161

    minutes, hours, doms, months, dows = (
        parse_cron_field(part, low, high) for part, (low, high) in zip(schedule.split(), CRON_BOUNDS)
    )
    zone = timezone.utc if tz is None else ZoneInfo(tz)
    # Walk candidates on the *local* calendar of ``zone`` so field matching —
    # the weekday in particular — follows the authoring wall clock. Ascending
    # wall order maps to ascending UTC order under a fixed fold policy, so
    # "strictly after" stays a plain epoch comparison.
    day = datetime.fromtimestamp(after_ms / 1000.0, zone).date()
    for offset in range(_CRON_SEARCH_LIMIT_DAYS):
        candidate_day = day + timedelta(days=offset)
        if not _day_matches(candidate_day, doms, months, dows):
            continue
        for hour in sorted(hours) if hours is not None else range(24):
            for minute in sorted(minutes) if minutes is not None else range(60):
                fire_ms = _local_wall_time_to_epoch_ms(candidate_day, hour, minute, zone)
                if fire_ms is not None and fire_ms > after_ms:
                    return fire_ms
    raise ValueError(f"cron schedule has no matching time within search window: {schedule!r}")


def _local_wall_time_to_epoch_ms(day: date, hour: int, minute: int, zone: tzinfo) -> int | None:
    """Resolve one local wall time to a UTC epoch-ms instant.

    DST policy (#953): a nonexistent wall time — the spring-forward gap —
    returns ``None`` so the occurrence is skipped; ``ZoneInfo`` would
    otherwise silently map it past the transition (PEP 495) and run it. An
    ambiguous wall time — the fall-back fold — resolves with ``fold=0``, the
    first occurrence, so it runs exactly once.
    """
    local = datetime(day.year, day.month, day.day, hour, minute, tzinfo=zone)
    as_utc = local.astimezone(timezone.utc)
    if as_utc.astimezone(zone).replace(tzinfo=None) != local.replace(tzinfo=None):
        return None  # wall time does not exist in this zone (DST gap)
    return int(as_utc.timestamp() * 1000)

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Pick a fire time that exists year-round in the job's timezone (avoid 02:00-03:00 style hours near common DST shifts).
  2. Run jobs in UTC unless local-time firing is required.
  3. If the hour must be local, schedule a minute that is not skipped (e.g. 02:45 often survives a 1-hour gap shift).

Example fix

# before
schedule = '0 2 * * *'; tz = 'America/Sao_Paulo'
# after
schedule = '0 4 * * *'; tz = 'America/Sao_Paulo'
Defensive patterns

Strategy: validation

Validate before calling

from scheduled_research.models import validate_schedule
validate_schedule(schedule)
# avoid fire hours inside common DST gaps
assert not (schedule.startswith('0 2 ') or schedule.startswith('0 30 2'))

Try / catch

try:
    due = job.next_due(after_ms)
except ValueError as e:
    if 'no matching time' in str(e):
        mark_job_schedule_failed(job)  # per-job failure, not a crash

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/ba4bc0f194e69a44. Report an issue: GitHub.