{"record":{"id":"baa9e8650b3e71aa","repo":"langchain-ai/deepagents","slug":"schedule-duration-must-be-a-positive-integer","errorCode":null,"errorMessage":"schedule duration must be a positive integer","messagePattern":"schedule duration must be a positive integer","errorType":"validation","errorClass":"CronJobError","httpStatus":null,"severity":"error","filePath":"libs/talon/deepagents_talon/cron/jobs.py","lineNumber":693,"sourceCode":"\ndef _parse_duration_minutes(value: str) -> int:\n    parts = value.split()\n    if len(parts) != 1:\n        msg = \"schedule duration must be a single value such as '30m'\"\n        raise CronJobError(msg)\n    text = parts[0]\n    if text.endswith(\"m\"):\n        return _positive_int(text[:-1])\n    if text.endswith(\"h\"):\n        return _positive_int(text[:-1]) * 60\n    msg = \"schedule duration must use 'm' for minutes or 'h' for hours\"\n    raise CronJobError(msg)\n\n\ndef _positive_int(value: str) -> int:\n    if not value.isdecimal():\n        msg = \"schedule duration must be a positive integer\"\n        raise CronJobError(msg)\n    number = int(value)\n    if number < MIN_GRANULARITY_MINUTES:\n        msg = \"schedule duration must be at least 1 minute\"\n        raise CronJobError(msg)\n    return number\n\n\ndef _same_origin_scope(left: CronOrigin, right: CronOrigin) -> bool:\n    return left.conversation_id == right.conversation_id and left.channel == right.channel\n\n\ndef _coerce_utc(value: datetime | None = None) -> datetime:\n    if value is None:\n        return datetime.now(UTC)\n    if value.tzinfo is None:\n        return value.replace(tzinfo=UTC)\n    return value.astimezone(UTC)\n","sourceCodeStart":675,"sourceCodeEnd":711,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/talon/deepagents_talon/cron/jobs.py#L675-L711","documentation":"CronJobError raised by `_positive_int` in libs/talon/deepagents_talon/cron/jobs.py when the numeric part of a schedule duration is not a decimal integer. `_parse_duration_minutes` strips the 'm'/'h' suffix and delegates here; `str.isdecimal()` must be true. This rejects negatives, signs, whitespace, floats, and non-ASCII digits.","triggerScenarios":"Passing a schedule like '-5m', ' 10m', '1.5h', '+3h', or 'abc' to `parse`. After stripping the unit suffix, the remaining text fails `isdecimal()` and this error is raised.","commonSituations":"Hand-edited config files with stray spaces or typo characters; floats used for sub-minute precision (e.g. '0.5h'); copy-paste artifacts like en-dashes or non-breaking spaces in the duration.","solutions":["Use a plain non-negative decimal integer before the unit: '5m', '60m', '2h'.","Convert fractional hours to whole minutes (e.g. '1.5h' -> '90m').","Strip whitespace and invisible characters from the schedule string before passing it in."],"exampleFix":"// before\nparse(\"1.5h\", ...)\nparse(\" 30m\", ...)\n// after\nparse(\"90m\", ...)\nparse(\"30m\", ...)","handlingStrategy":"validation","validationCode":"def validate_duration_number(text: str) -> bool:\n    unit, num = text[-1:], text[:-1]\n    return unit in (\"m\", \"h\") and num.isdecimal() and int(num) >= 1","typeGuard":"def is_decimal_duration(text: str) -> bool:\n    return isinstance(text, str) and len(text) > 1 and text[:-1].isdecimal()","tryCatchPattern":"try:\n    job = parse(schedule)\nexcept CronJobError as exc:\n    raise ValueError(f\"schedule {schedule!r} must be an integer like '30m' or '2h'\") from exc","preventionTips":["Strip whitespace before passing duration strings.","Avoid floats like '1.5h' — precompute '90m'.","Beware copy-paste characters (en-dash, non-breaking space) that break isdecimal()."],"tags":["cron","validation","integer-parsing"],"backgroundTag":"invalid-duration-format","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}