{"record":{"id":"97ddd07a27a580b9","repo":"NousResearch/hermes-agent","slug":"invalid-cron-expression-schedule-e","errorCode":null,"errorMessage":"Invalid cron expression '{schedule}': {e}","messagePattern":"Invalid cron expression '(.+?)': (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"cron/jobs.py","lineNumber":656,"sourceCode":"        return {\n            \"kind\": \"interval\",\n            \"minutes\": minutes,\n            \"display\": f\"every {minutes}m\"\n        }\n    \n    # Check for cron expression (5 or 6 space-separated fields)\n    # Cron fields: minute hour day month weekday [year]\n    parts = schedule.split()\n    if len(parts) >= 5 and all(\n        re.match(r'^[\\d\\*\\-,/]+$', p) for p in parts[:5]\n    ):\n        if not _ensure_croniter():\n            raise ValueError(\"Cron expressions require 'croniter' package. Install with: pip install croniter\")\n        # Validate cron expression\n        try:\n            croniter(schedule)\n        except Exception as e:\n            raise ValueError(f\"Invalid cron expression '{schedule}': {e}\")\n        return {\n            \"kind\": \"cron\",\n            \"expr\": schedule,\n            \"display\": schedule\n        }\n    \n    # ISO timestamp (contains T or looks like date)\n    if 'T' in schedule or re.match(r'^\\d{4}-\\d{2}-\\d{2}', schedule):\n        try:\n            # Parse and validate\n            dt = datetime.fromisoformat(schedule.replace('Z', '+00:00'))\n            # Make naive timestamps timezone-aware at parse time so the stored\n            # value doesn't depend on the system timezone matching at check time.\n            #\n            # Anchor to the CONFIGURED Hermes timezone, not the server's local\n            # timezone. The due-check (`get_due_jobs`) compares `next_run_at`\n            # against `hermes_time.now()`, which uses the configured zone. If a\n            # naive \"20:07\" were interpreted as server-local (e.g. UTC) while","sourceCodeStart":638,"sourceCodeEnd":674,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/cron/jobs.py#L638-L674","documentation":"ValueError from parse_schedule's cron branch: croniter IS available, the string shaped like a cron expression (5-6 fields of digits/*/-/,/), but croniter's own validation rejected it. The underlying croniter exception text is appended, typically naming the invalid field (e.g. minute values out of range, bad step).","triggerScenarios":"create_job(schedule='61 9 * * *') (minute > 59), schedule='0 25 * * *' (hour > 23), schedule='*/0 * * * *' (zero step), or malformed field lists like '1,,2 * * * *'.","commonSituations":"Hand-written cron expressions with out-of-range fields; 6-field expressions with a bad year; copy-paste crontabs with usernames or @-tokens (those don't match the field regex and route elsewhere or fail differently); assuming 24:00 or 32nd day are accepted.","solutions":["Fix the field croniter names in the message — minute 0-59, hour 0-23, dom 1-31, month 1-12, dow 0-7","Validate locally first: from croniter import croniter; croniter(expr) in a try/except before submitting","For simple daily jobs consider a duration or blueprint schedule instead of raw cron"],"exampleFix":"# before\ncreate_job(schedule=\"61 9 * * *\")\n# ValueError: Invalid cron expression '61 9 * * *': [minute] ... \n\n# after\ncreate_job(schedule=\"1 9 * * *\")","handlingStrategy":"validation","validationCode":"from croniter import croniter\n\ndef is_valid_cron(expr: str) -> bool:\n    try:\n        croniter(expr)\n        return True\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    parsed = parse_schedule(expr)\nexcept ValueError as e:\n    if str(e).startswith(\"Invalid cron expression\"):\n        expr = fix_cron_fields(expr)  # e.g. clamp minute 61 -> 1, prompt user\n        parsed = parse_schedule(expr)\n    else:\n        raise","preventionTips":["Validate with croniter() in a try/except before create_job","Keep fields in range: minute 0-59, hour 0-23, dom 1-31, month 1-12, dow 0-7","Prefer the blueprint/duration schedule forms for simple recurring jobs"],"tags":["cron","cron-expression","validation","parsing"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}