odysseus-dev/odysseus · warning · HTTPException
Invalid cron expression
Error message
Invalid cron expression
What it means
Create-task validation: the supplied cron_expression was handed to croniter and raised (CroniterBadCronError or similar), meaning it is not parseable cron syntax. The bare except Exception converts it to HTTP 400 'Invalid cron expression'.
Source
Thrown at routes/task_routes.py:473
# Validate
if req.task_type in ("llm", "research") and not req.prompt:
raise HTTPException(400, "Prompt is required for LLM/research tasks")
if req.task_type == "action" and not req.action:
raise HTTPException(400, "Action name is required for action tasks")
# Block shell-executing action types for non-admins. action_run_local
# uses subprocess.run(shell=True) and ssh_command / run_script run
# arbitrary commands.
_require_admin_for_task_action(user, req.task_type, req.action)
if req.trigger_type == "schedule" and not req.schedule:
raise HTTPException(400, "Schedule is required for schedule-triggered tasks")
if req.trigger_type == "schedule" and req.schedule == "cron" and not req.cron_expression:
raise HTTPException(400, "Cron expression is required for cron schedule")
if req.trigger_type == "schedule" and req.schedule == "cron" and req.cron_expression:
try:
from croniter import croniter
croniter(req.cron_expression)
except Exception:
raise HTTPException(400, "Invalid cron expression")
if req.trigger_type == "event" and not req.trigger_event:
raise HTTPException(400, "Event name is required for event-triggered tasks")
if req.trigger_type == "event" and not req.trigger_count:
raise HTTPException(400, "Trigger count is required for event-triggered tasks")
# Auto-generate name
name = req.name
if not name:
if req.task_type == "action":
from src.builtin_actions import BUILTIN_ACTION_INFO
name = BUILTIN_ACTION_INFO.get(req.action, req.action or "Action Task")
elif req.prompt:
name = await _generate_task_name(req.prompt, owner=user)
else:
name = "Untitled Task"
# Compute next_run for schedule-triggered tasks
next_run = NoneView on GitHub (pinned to f9235ebbf1)
Solutions
- Use exactly five space-separated fields: minute hour day-of-month month day-of-week (e.g. '0 9 * * *').
- Validate locally with the same library: pip install croniter; croniter(expr) must not raise before submitting.
- If you need seconds or years, check whether the deployment's croniter/scheduler supports extended syntax; otherwise simplify to 5 fields.
- For Quartz-style expressions, translate to 5-field cron first.
Example fix
# before
req = {"trigger_type": "schedule", "schedule": "cron", "cron_expression": "every 5 min"}
# after
from croniter import croniter
expr = "*/5 * * * *"
croniter(expr) # raises here, before the API call
req = {"trigger_type": "schedule", "schedule": "cron", "cron_expression": expr} Defensive patterns
Strategy: validation
Validate before calling
const CRON_RE = /^(\S+\s+){4}\S+$/;
function validCron(expr) {
if (!CRON_RE.test(expr.trim())) return false;
// strongest check: run croniter server-side / via a cron parser lib client-side
try { cronParser.parseExpression(expr); return true; } catch { return false; }
} Try / catch
try { await api.createTask(p); }
catch (e) {
if (e.status === 400 && /cron/i.test(e.message)) { highlightCronField(e.message); return; }
throw e;
} Prevention
- Use a cron builder UI (minute/hour/day pickers) instead of a free-text field.
- Validate with the same engine (croniter) in CI for all seeded cron expressions.
- Reject Quartz/systemd-timer syntax at the input layer with a clear hint about 5-field cron.
When it happens
Trigger: POST /api/tasks with schedule 'cron' and a malformed expression: wrong field count ('0 9 * *' has 4), non-numeric garbage ('every 5 minutes'), 6+ fields, or ranges/steps croniter rejects ('5-1 * * * *').
Common situations: Human-language schedule typed into a cron field; copy-paste from systemd timers or Quartz (6/7-field) syntax that croniter rejects by default; locale decimal separators or smart quotes in the expression; UI builder generating '0 9 * * * ' with stray whitespace variants.
Related errors
- Cron expression is required for cron schedule
- Task cannot chain to itself
- Prompt is required for LLM/research tasks
- Action name is required for action tasks
- Schedule is required for schedule-triggered tasks
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/df2afffcb307e171.
Report an issue: GitHub.