nanocoai/nanoclaw · error
invalid --recurrence: ${msg}
Error message
invalid --recurrence: ${msg} What it means
Thrown when the --recurrence value passed to validateRecurrence fails to parse as a valid cron expression via CronExpressionParser (cron-parser). The original parser message is embedded so the user sees exactly which cron field is invalid. It is thrown before any task is created, acting as input validation for scheduled task recurrence.
Source
Thrown at src/modules/scheduling/create.ts:73
const hex = (n: number): string => randomUUID().replace(/-/g, '').slice(0, n);
const slug = taskNameSlug(name);
return slug ? `${slug}-${hex(4)}` : `t-${hex(6)}`;
}
export function parseProcessAfter(value: unknown, tz: string = TIMEZONE): string {
if (typeof value !== 'string' || value.length === 0) throw new Error('--process-after is required');
const date = parseZonedToUtc(value, tz);
if (Number.isNaN(date.getTime())) throw new Error(`invalid --process-after: ${value}`);
return date.toISOString();
}
export function validateRecurrence(value: string | null | undefined, tz: string = TIMEZONE): void {
if (!value) return;
try {
CronExpressionParser.parse(value, { tz });
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
throw new Error(`invalid --recurrence: ${msg}`, { cause: err });
}
}
export function enforceRecurrenceLimit(
recurrence: string | null,
override: boolean,
hasScript: boolean,
tz: string = TIMEZONE,
): void {
// A gate script is the sanctioned mitigation: a skipped fire costs no agent
// tokens, so scripted tasks may poll faster without the explicit override.
if (!recurrence || override || hasScript) return;
const horizon = Date.now() + 24 * 60 * 60 * 1000;
const interval = CronExpressionParser.parse(recurrence, { tz });
let fires = 0;
while (fires <= MAX_DAILY_FIRES) {
const next = interval.next();
if (next.getTime() > horizon) break;View on GitHub (pinned to 294ef2aee8)
Solutions
- Fix the recurrence string to be a valid cron expression for cron-parser (typically 5 or 6 fields: '0 9 * * 1-5' for weekdays at 9am).
- Verify expression validity in a scratch script: import { CronExpressionParser } from 'cron-parser' and parse it directly to see the raw parser error.
- If you meant an ISO duration or 'every N minutes' style, convert to cron ('*/15 * * * *' for every 15 minutes).
- Check that the timezone argument is a valid IANA name — an invalid tz can surface as a parse failure.
Example fix
// before
await prepareScheduledTask({ prompt: 'daily report', recurrence: 'every day at 9' });
// after
await prepareScheduledTask({ prompt: 'daily report', recurrence: '0 9 * * *' }); Defensive patterns
Strategy: validation
Validate before calling
import { CronExpressionParser } from 'cron-parser';
function isValidCron(expr: string, tz?: string): boolean {
try { CronExpressionParser.parse(expr, { tz }); return true; } catch { return false; }
} Type guard
const isValidCron = (e: string, tz?: string): boolean => { try { CronExpressionParser.parse(e, { tz }); return true; } catch { return false; } }; Try / catch
try { validateRecurrence(recurrence, tz); } catch (err) { throw new Error(`Bad --recurrence "${recurrence}": ${(err as Error).message}`, { cause: err }); } Prevention
- Validate cron strings at the config/CLI boundary, not at task creation.
- Keep a lint rule or test asserting all shipped recurrence strings parse.
- Document the expected cron field count for your cron-parser version.
When it happens
Trigger: Calling prepareScheduledTask or updateTaskCommand with a recurrence string that is not valid cron syntax (e.g. 'every day', '@daily' if unsupported by the parser version, '0 0 * *' with too few fields), or with a 5-field cron when the parser is configured for 6-field, or an invalid timezone making the expression unparseable in that tz.
Common situations: Users coming from systemd timers, ISO 8601 durations (R/PT1H), or @-shorthand cron who assume the scheduler accepts them; copy-pasting a 5-field crontab entry into a tool expecting 6 fields; typos like '0 0 * * * * *' (too many fields) or '* * */e * *'.
Related errors
- --prompt is required
- --recurrence has no upcoming run: ${recurrence}
- --status must be pending or paused
- session not found: ${sessionId}
- task series id is required
AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28).
Data as JSON: /api/errors/d8fed21320a6009e.
Report an issue: GitHub.