HKUDS/Vibe-Trading · error · ValueError
cron field {part!r} is not valid; each field must be *, */n,
Error message
cron field {part!r} is not valid; each field must be *, */n, or a comma-separated list of numbers and low-high ranges What it means
Each cron field must be '*', '*/n', or a comma-separated list of atoms matching a number or low-high range. Anything else — names (MON), L/W flags, @-shorthands, 6-field seconds, or stray characters — fails the atom regex and is rejected by this simplified 5-field parser.
Source
Thrown at agent/src/scheduled_research/models.py:57
# either end of a range — is validated against these bounds so out-of-range
# values (e.g. minute ``99``) are rejected. Day-of-week uses the cron
# convention Sunday == 0; ``7`` is not accepted as a Sunday alias.
CRON_BOUNDS = ((0, 59), (0, 23), (1, 31), (1, 12), (0, 6))
def _validate_cron_field(part: str, low: int, high: int) -> None:
"""Raise ``ValueError`` when one cron field is malformed or out of range."""
if part == "*":
return
if _CRON_STEP_RE.fullmatch(part):
value = int(part[2:])
if not low <= value <= high:
raise ValueError(f"cron field {part!r} is out of range; expected {low}-{high}")
return
for atom in part.split(","):
match = _CRON_ATOM_RE.fullmatch(atom)
if match is None:
raise ValueError(
f"cron field {part!r} is not valid; each field must be *, */n, "
f"or a comma-separated list of numbers and low-high ranges"
)
start = int(match.group(1))
end = int(match.group(2)) if match.group(2) is not None else start
if start > end:
raise ValueError(f"cron range {atom!r} is reversed; expected low-high")
if start < low or end > high:
raise ValueError(f"cron field {atom!r} is out of range; expected {low}-{high}")
def parse_cron_field(part: str, low: int, high: int) -> Optional[Set[int]]:
"""Expand one validated cron field into its matching values.
Kept beside :func:`validate_schedule` so the accepted grammar and the
executor's evaluation of it can never drift apart.
Args:View on GitHub (pinned to 80ffdda44c)
Solutions
- Convert names to numbers: MON->1, JAN->1, SUN->0 or 7 per your convention (here day-of-week is 0-6).
- Remove unsupported flags (@yearly, L, W, #, ?) and extra fields; this parser wants exactly 5 numeric fields.
- For fixed intervals, use the milliseconds form instead of cron.
Example fix
# before
validate_schedule('0 0 * * MON-FRI')
# after
validate_schedule('0 0 * * 1-5') Defensive patterns
Strategy: validation
Validate before calling
import re
CRON_OK = re.compile(r"^(\*|(\*/\d+)|([0-9]+(-[0-9]+)?)(,[0-9]+(-[0-9]+)*)*)\s*$")
def fields_shape_ok(s: str) -> bool:
parts = s.split()
return len(parts) == 5 and all(CRON_OK.fullmatch(p) for p in parts) Type guard
def is_supported_cron(s: str) -> bool:
import re
atom = r'(\d+(-\d+)?)'
return bool(re.fullmatch(r'(\*|\*/\d+|' + atom + r'(,' + atom + r')*)', s)) Try / catch
try:
validate_schedule(schedule)
except ValueError as e:
raise HTTPError(400, str(e)) from e Prevention
- Use numeric cron only: no names, L/W/#/? flags, or @-shorthands.
- Translate Quartz 6-field strings to 5 fields before submitting.
When it happens
Trigger: validate_schedule('0 0 * * MON'), '*/5 0 * * 1-5/2', '0 0 * * 1#3', or a Quartz-style '0 0 12 ? * MON-FR' string.
Common situations: Copying a cron string from crontab examples, Quartz, or cloud schedulers that support extended syntax; LLM- or user-supplied schedules using day names; assuming seconds fields exist.
Related errors
- cron field {part!r} is out of range; expected {low}-{high}
- cron range {atom!r} is reversed; expected low-high
- cron field {atom!r} is out of range; expected {low}-{high}
- schedule must be a non-empty string
- interval is too large; expected at most 15 digits of millise
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/ea75b7463333747c.
Report an issue: GitHub.