nanocoai/nanoclaw · error
--recurrence has no upcoming run: ${recurrence}
Error message
--recurrence has no upcoming run: ${recurrence} What it means
For a recurring task with no explicit processAfter, the scheduler computes the first run as the cron expression's next execution time. If cron-parser's next() returns a falsy value (no future match), the recurrence can never fire, so the task is rejected with '--recurrence has no upcoming run'. This typically happens for expressions whose only matches are in the past or that are inherently unsatisfiable.
Source
Thrown at src/modules/scheduling/create.ts:122
name?: string;
prompt: string;
recurrence?: string | null;
processAfter?: string;
script?: string | null;
dangerouslyOverrideRecurrenceLimit?: boolean;
timezone?: string;
}): PreparedScheduledTask {
if (!input.prompt) throw new Error('--prompt is required');
const recurrence = input.recurrence ?? null;
const script = input.script ?? null;
const tz = input.timezone ?? TIMEZONE;
validateRecurrence(recurrence, tz);
enforceRecurrenceLimit(recurrence, input.dangerouslyOverrideRecurrenceLimit === true, script !== null, tz);
let processAfter: string;
if (input.processAfter === undefined && recurrence) {
const next = CronExpressionParser.parse(recurrence, { tz }).next().toISOString();
if (!next) throw new Error(`--recurrence has no upcoming run: ${recurrence}`);
processAfter = next;
} else {
processAfter = parseProcessAfter(input.processAfter, tz);
}
return { name: input.name, prompt: input.prompt, recurrence, script, processAfter };
}
/** Persist a prepared task through NanoClaw's single task/session representation. */
export async function createScheduledTask(
agentGroupId: string,
task: PreparedScheduledTask,
options?: { status?: 'pending' | 'paused'; originSessionId?: string | null },
): Promise<{ session: { id: string; agent_group_id: string }; row: ScheduledTaskRow }> {
const id = makeTaskId(task.name);
const { session } = await resolveTaskSession(agentGroupId, id);
const row = await withMailboxSession(agentGroupId, session.id, async (db) => {View on GitHub (pinned to 294ef2aee8)
Solutions
- Test the expression directly: CronExpressionParser.parse(recurrence, { tz }).next() in a scratch script to confirm it yields a date.
- Fix impossible field combinations (day-of-month/day-of-week/year) so at least one future instant matches.
- If you want the task to start at a specific time regardless, pass processAfter explicitly and let recurrence govern subsequent runs.
- If the recurrence is genuinely one-shot-in-the-past, drop recurrence and use processAfter alone.
Example fix
// before
await prepareScheduledTask({ prompt: 'x', recurrence: '0 0 31 2 *' });
// after
await prepareScheduledTask({ prompt: 'x', recurrence: '0 0 1 * *' }); Defensive patterns
Strategy: validation
Validate before calling
import { CronExpressionParser } from 'cron-parser';
const hasNextRun = (expr: string, tz?: string): boolean => { try { return CronExpressionParser.parse(expr, { tz }).next() !== undefined; } catch { return false; } }; Try / catch
try { await prepareScheduledTask(input); } catch (err) { if ((err as Error).message.includes('no upcoming run')) throw new Error(`Recurrence never fires: ${input.recurrence}`, { cause: err }); throw err; } Prevention
- Sanity-check next() in a dry-run before scheduling.
- Avoid exotic cron fields (year, contradictory DOM/DOW) unless proven to fire.
When it happens
Trigger: Passing a cron expression with a bounded/past-only component, e.g. a specific past date field, or a constraint cron-parser evaluates as having no next occurrence (certain day-of-month/day-of-week contradictions or year fields in the past). Recurrence is set and processAfter is undefined, so the next() branch is taken.
Common situations: Hand-written crons with impossible field combinations (e.g. '0 0 31 2 *' — Feb 31); expressions carrying a past year; migrating from a system that accepted never-matching expressions silently.
Related errors
- invalid --recurrence: ${msg}
- --prompt is required
- --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/70f39be093070f49.
Report an issue: GitHub.