{"record":{"id":"2c5b3841864bce55","repo":"odysseus-dev/odysseus","slug":"trigger-count-is-required-for-event-triggered-task","errorCode":null,"errorMessage":"Trigger count is required for event-triggered tasks","messagePattern":"Trigger count is required for event-triggered tasks","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"routes/task_routes.py","lineNumber":477,"sourceCode":"            raise HTTPException(400, \"Action name is required for action tasks\")\n        # Block shell-executing action types for non-admins. action_run_local\n        # uses subprocess.run(shell=True) and ssh_command / run_script run\n        # arbitrary commands.\n        _require_admin_for_task_action(user, req.task_type, req.action)\n        if req.trigger_type == \"schedule\" and not req.schedule:\n            raise HTTPException(400, \"Schedule is required for schedule-triggered tasks\")\n        if req.trigger_type == \"schedule\" and req.schedule == \"cron\" and not req.cron_expression:\n            raise HTTPException(400, \"Cron expression is required for cron schedule\")\n        if req.trigger_type == \"schedule\" and req.schedule == \"cron\" and req.cron_expression:\n            try:\n                from croniter import croniter\n                croniter(req.cron_expression)\n            except Exception:\n                raise HTTPException(400, \"Invalid cron expression\")\n        if req.trigger_type == \"event\" and not req.trigger_event:\n            raise HTTPException(400, \"Event name is required for event-triggered tasks\")\n        if req.trigger_type == \"event\" and not req.trigger_count:\n            raise HTTPException(400, \"Trigger count is required for event-triggered tasks\")\n\n        # Auto-generate name\n        name = req.name\n        if not name:\n            if req.task_type == \"action\":\n                from src.builtin_actions import BUILTIN_ACTION_INFO\n                name = BUILTIN_ACTION_INFO.get(req.action, req.action or \"Action Task\")\n            elif req.prompt:\n                name = await _generate_task_name(req.prompt, owner=user)\n            else:\n                name = \"Untitled Task\"\n\n        # Compute next_run for schedule-triggered tasks\n        next_run = None\n        sched_date = None\n        if req.trigger_type == \"schedule\":\n            if req.schedule == \"once\" and req.scheduled_date:\n                try:","sourceCodeStart":459,"sourceCodeEnd":495,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/task_routes.py#L459-L495","documentation":"Create-task validation: trigger_type 'event' additionally requires trigger_count (how many times/how many occurrences the event trigger consumes) to be set — a falsy value (0, null, missing) is rejected with HTTP 400.","triggerScenarios":"POST /api/tasks with {\"trigger_type\": \"event\", \"trigger_event\": \"...\"} but trigger_count omitted, null, or 0.","commonSituations":"Client sends trigger_count only for recurring mode; form defaults the count field to 0; schema change from optional to required trigger_count after an upgrade so old payloads now fail.","solutions":["Include a positive integer trigger_count (e.g. 1) whenever trigger_type is 'event'.","Default the count field to 1 in the client form so it is always serialized.","After upgrading the backend, re-check the TaskCreate schema for newly required event fields."],"exampleFix":"// before\n{trigger_type: 'event', trigger_event: 'email.received'}\n\n// after\n{trigger_type: 'event', trigger_event: 'email.received', trigger_count: 1}","handlingStrategy":"validation","validationCode":"function validateEventCount(p) {\n  if (p.trigger_type === 'event' && !(Number(p.trigger_count) > 0))\n    throw new ValidationError('Trigger count must be a positive integer');\n  return {...p, trigger_count: Number(p.trigger_count)};\n}","typeGuard":"function hasTriggerCount(p: unknown): p is {trigger_type: 'event'; trigger_count: number} {\n  return typeof p === 'object' && p !== null &&\n    typeof (p as any).trigger_count === 'number' && (p as any).trigger_count > 0;\n}","tryCatchPattern":null,"preventionTips":["Default the count input to 1 and always serialize it for event tasks.","After backend upgrades, re-read the TaskCreate schema for newly required fields."],"tags":["tasks","validation","http-400","events"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}