HKUDS/Vibe-Trading · error · ValueError

schedule must be a positive integer (ms) or a 5-field cron s

Error message

schedule must be a positive integer (ms) or a 5-field cron string; got: {schedule!r}

What it means

After the interval form is ruled out, the schedule must split into exactly 5 whitespace-separated cron fields (minute hour day-of-month month day-of-week). Any other field count — 6-field Quartz strings with seconds, single-word shorthands like '@daily', or truncated strings — is rejected.

Source

Thrown at agent/src/scheduled_research/models.py:137

        schedule: Either a positive integer string (interval-ms) or a
            simplified 5-field cron expression.

    Raises:
        ValueError: When the schedule does not match either accepted form.
    """
    if not schedule or not isinstance(schedule, str):
        raise ValueError("schedule must be a non-empty string")

    if _INTERVAL_MS_RE.fullmatch(schedule.strip()):
        # 15 digits ≈ 31,000 years in milliseconds — anything longer is not a
        # usable interval and would only feed int() conversion of huge strings.
        if len(schedule.strip()) > 15:
            raise ValueError("interval is too large; expected at most 15 digits of milliseconds")
        return  # valid interval

    parts = schedule.strip().split()
    if len(parts) != _CRON_PARTS:
        raise ValueError(f"schedule must be a positive integer (ms) or a 5-field cron string; got: {schedule!r}")
    for part, (low, high) in zip(parts, CRON_BOUNDS):
        _validate_cron_field(part, low, high)


def validate_timezone_shape(tz: Optional[str]) -> None:
    """Raise ``ValueError`` when *tz* is not ``None`` or a non-empty string.

    This is the persistence-level check: it deliberately does NOT resolve the
    key, because resolvability depends on the host's timezone database. A
    store written where a key resolved must keep loading and persisting on a
    host where it does not; the executor surfaces the unresolvable key as a
    per-job schedule failure instead.
    """
    if tz is None:
        return
    if not isinstance(tz, str) or not tz.strip():
        raise ValueError("timezone must be a non-empty IANA timezone key or null")

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Rewrite as exactly 5 fields: drop the leading seconds field, translate @daily to '0 0 * * *', @hourly to '0 * * * *'.
  2. For fixed-rate schedules, use the millisecond interval form instead of a 6-field expression.
  3. Validate user-supplied schedules with validate_schedule at ingestion time.

Example fix

# before
validate_schedule('0 */2 * * * *')
# after
validate_schedule('*/120 * * * *')  # or '7200000' ms
Defensive patterns

Strategy: validation

Validate before calling

parts = schedule.split()
if len(parts) == 6:
    schedule = ' '.join(parts[1:])  # drop Quartz seconds field
SHORTHAND = {'@hourly': '0 * * * *', '@daily': '0 0 * * *', '@weekly': '0 0 * * 0'}
schedule = SHORTHAND.get(schedule, schedule)
validate_schedule(schedule)

Type guard

def is_five_field_cron(s: str) -> bool:
    return isinstance(s, str) and len(s.split()) == 5

Try / catch

try:
    validate_schedule(schedule)
except ValueError as e:
    return bad_request(f'invalid schedule: {e}')

Prevention

When it happens

Trigger: validate_schedule('* * * * * *') (6 fields), validate_schedule('@hourly'), validate_schedule('0 9 * *') (4 fields), or tabs/multiple spaces are fine but wrong field count still fails.

Common situations: Copying a Quartz/CloudWatch/EventBridge expression that includes a seconds field; using @-shorthands from Vixie cron; trailing truncation when a schedule string is cut off in config or logging.

Related errors


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