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

  1. Test the expression directly: CronExpressionParser.parse(recurrence, { tz }).next() in a scratch script to confirm it yields a date.
  2. Fix impossible field combinations (day-of-month/day-of-week/year) so at least one future instant matches.
  3. If you want the task to start at a specific time regardless, pass processAfter explicitly and let recurrence govern subsequent runs.
  4. 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

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


AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28). Data as JSON: /api/errors/70f39be093070f49. Report an issue: GitHub.