{"record":{"id":"490ee3b431732437","repo":"langchain-ai/deepagents","slug":"schedule-must-look-like-in-30m-or-every-15m","errorCode":null,"errorMessage":"schedule must look like 'in 30m' or 'every 15m'","messagePattern":"schedule must look like 'in 30m' or 'every 15m'","errorType":"validation","errorClass":"CronJobError","httpStatus":null,"severity":"error","filePath":"libs/talon/deepagents_talon/cron/jobs.py","lineNumber":150,"sourceCode":"    def parse(cls, value: str) -> CronSchedule:\n        \"\"\"Parse a supported schedule string.\n\n        Args:\n            value: Schedule text such as `in 30m` or `every 15m`.\n\n        Returns:\n            Parsed schedule.\n\n        Raises:\n            CronJobError: If the schedule string is unsupported.\n        \"\"\"\n        text = \" \".join(value.strip().lower().split())\n        if text.startswith(\"in \"):\n            return cls(kind=\"one_shot\", minutes=_parse_duration_minutes(text[3:]), display=value)\n        if text.startswith(\"every \"):\n            return cls(kind=\"recurring\", minutes=_parse_duration_minutes(text[6:]), display=value)\n        msg = \"schedule must look like 'in 30m' or 'every 15m'\"\n        raise CronJobError(msg)\n\n    def next_after(self, now: datetime) -> datetime:\n        \"\"\"Return the next scheduled run after `now`.\n\n        Args:\n            now: Current timestamp.\n\n        Returns:\n            Next run timestamp.\n        \"\"\"\n        return now + timedelta(minutes=self.minutes)\n\n    def to_dict(self) -> CronScheduleDict:\n        \"\"\"Serialize this schedule for disk storage.\n\n        Returns:\n            JSON-compatible schedule dictionary.\n        \"\"\"","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/talon/deepagents_talon/cron/jobs.py#L132-L168","documentation":"This error is raised by CronSchedule.parse when the given schedule string does not start with either 'in ' (one-shot delay) or 'every ' (recurring interval) after lowercasing and whitespace-normalizing. The library only supports these two human-readable relative-duration formats, and anything else (including absolute times, cron expressions, or unsupported prefixes like 'at ' or 'daily') is rejected. It is a CronJobError, so callers can catch it specifically.","triggerScenarios":"Calling parse (directly or via create_job/edit_job) with a value like '30m', 'daily', 'at 5pm', 'every', 'in', or 'Every 15 MINUTES ' where the normalized text lacks the required 'in ' or 'every ' prefix. A trailing/leading whitespace-only string or an empty string also lands here.","commonSituations":"Passing a cron expression ('0 9 * * *') or ISO timestamp instead of a relative duration; forgetting the 'in '/'every ' prefix while supplying just the duration ('30m'); building UI input that lets users type arbitrary schedule text; locale-formatted strings with uppercase letters or extra spaces that survive normalization but have the wrong prefix.","solutions":["Prefix the duration with 'in ' for one-shot schedules or 'every ' for recurring ones, e.g. 'in 30m' or 'every 15m'","Normalize the value yourself before calling parse: text = ' '.join(value.strip().lower().split()) and prepend the missing prefix based on intent","If the user supplied an absolute time or cron expression, convert it to a relative duration first — parse does not support those formats","Catch CronJobError and surface the accepted format hint to the end user instead of crashing"],"exampleFix":"// before\nschedule = CronSchedule.parse(\"30m\")\n// after\nschedule = CronSchedule.parse(\"in 30m\")  # one-shot\n# or\nschedule = CronSchedule.parse(\"every 15m\")  # recurring","handlingStrategy":"validation","validationCode":"import re\n_DURATION = re.compile(r\"^(in|every)\\s+\\d+\\s*(s|m|h|d)$\")\n\ndef is_valid_schedule(value: str) -> bool:\n    text = \" \".join(value.strip().lower().split())\n    return _DURATION.match(text) is not None\n\nif not is_valid_schedule(user_input):\n    raise ValueError(f\"schedule must look like 'in 30m' or 'every 15m', got {user_input!r}\")","typeGuard":null,"tryCatchPattern":"try:\n    schedule = CronSchedule.parse(user_input)\nexcept CronJobError as exc:\n    logger.warning(\"invalid schedule %r: %s\", user_input, exc)\n    schedule = None  # or re-prompt / apply a default","preventionTips":["Always build schedule strings via the 'in <dur>' / 'every <dur>' templates instead of free text","Lowercase and strip/normalize whitespace before calling parse","Sanitize UI input against a duration regex before submitting","Never pass cron expressions or absolute timestamps to parse — convert them first"],"tags":["validation","cron","input-format"],"backgroundTag":"invalid-schedule-format","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}