actualbudget/actual · error

A date condition is required to create a schedule

Error message

A date condition is required to create a schedule

What it means

`createSchedule` requires at least one date condition among the passed rule conditions (`extractScheduleConds(conditions).date`). Schedules exist to run a rule on a recurring date, so a schedule without a date condition is invalid and rejected.

Source

Thrown at packages/loot-core/src/server/schedules/app.ts:333

  id: string;
  targetId: string | null;
}) {
  await db.moveSchedule(id, targetId);
  return {};
}

export async function createSchedule({
  schedule = null,
  conditions = [],
}: {
  schedule?: Partial<ScheduleEntity> | null;
  conditions?: RuleConditionEntity[];
} = {}): Promise<ScheduleEntity['id']> {
  const scheduleId = schedule?.id || uuidv4();

  const { date: dateCond } = extractScheduleConds(conditions);
  if (dateCond == null) {
    throw new Error('A date condition is required to create a schedule');
  }
  if (dateCond.value == null) {
    throw new Error('Date is required');
  }

  const nextDate = getNextDate(dateCond);
  const nextDateRepr = nextDate ? toDateRepr(nextDate) : null;
  const scheduleFields = schedule && {
    ...schedule,
    name: normalizeScheduleName(schedule.name),
  };
  if (scheduleFields) {
    if (scheduleFields.name) {
      if (await checkIfScheduleExists(scheduleFields.name, scheduleId)) {
        throw new Error('Cannot create schedules with the same name');
      }
    }
  }

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Add a `date` condition to the conditions array (op `isapprox` with a date or recurring descriptor)
  2. Verify condition `field` is exactly 'date' (extractScheduleConds matches on field name)
  3. Create the schedule via the UI to get a well-formed conditions payload

Example fix

// before
await createSchedule({ conditions: [{ field: 'payee', op: 'is', value: 'landlord' }] });
// after
await createSchedule({
  conditions: [
    { field: 'date', op: 'isapprox', value: { frequency: 'monthly', start: '2026-02-01' } },
    { field: 'payee', op: 'is', value: 'landlord' },
  ],
});
Defensive patterns

Strategy: validation

Validate before calling

function hasDateCondition(conditions = []) {
  return conditions.some(c => c.field === 'date' && c.value != null);
}
if (!hasDateCondition(conditions)) {
  throw new Error('createSchedule requires a date condition with a value');
}
await createSchedule({ conditions });

Type guard

function isDateCondition(c) {
  return c != null && c.field === 'date' &&
    (typeof c.op === 'string' && c.value != null);
}

Try / catch

try {
  const id = await createSchedule({ conditions });
} catch (e) {
  if (e.message === 'A date condition is required to create a schedule') {
    logger.error('Schedule conditions missing date condition', conditions);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling `createSchedule({ conditions: [...] })` where the conditions array contains only non-date conditions (e.g. only a payee or amount condition), or `conditions` is an empty array.

Common situations: API integrations building schedules with only account/payee matching conditions; forgetting that the recurring date itself must be expressed as a `date` condition with an isapprox/recurring value.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/adbb1cf73457808d. Report an issue: GitHub.