actualbudget/actual · error

You cannot change the rule of a schedule

Error message

You cannot change the rule of a schedule

What it means

`updateSchedule` refuses updates where `schedule.rule` is set, because the rule attached to a schedule is created once at schedule creation and is not meant to be swapped. Changing conditions should be done via the `conditions` parameter instead.

Source

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

    rule: ruleId,
  });

  return scheduleId;
}

// TODO: don't allow deleting rules that link schedules

export async function updateSchedule({
  schedule,
  conditions,
  resetNextDate,
}: {
  schedule: Partial<ScheduleEntity> & Pick<ScheduleEntity, 'id'>;
  conditions?: RuleConditionEntity[];
  resetNextDate?: boolean;
}) {
  if (schedule.rule) {
    throw new Error('You cannot change the rule of a schedule');
  }
  const scheduleFields = { ...schedule };
  if ('name' in scheduleFields) {
    scheduleFields.name = normalizeScheduleName(scheduleFields.name);
    if (
      scheduleFields.name &&
      (await checkIfScheduleExists(scheduleFields.name, scheduleFields.id))
    ) {
      throw new Error('Cannot update schedules with the same name');
    }
  }
  let rule;

  // This must be outside the `batchMessages` call because we change
  // and then read data
  if (conditions) {
    const { date: dateCond } = extractScheduleConds(conditions);
    if (dateCond && dateCond.value == null) {

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Remove the `rule` property from the object passed to updateSchedule
  2. Update rule behavior via the `conditions` parameter or by editing the rule itself
  3. Destructure only mutable fields (`id`, `name`, `completed`, etc.) before updating

Example fix

// before
await updateSchedule({ schedule: fetchedSchedule }); // includes .rule -> throws
// after
const { rule, ...editable } = fetchedSchedule;
await updateSchedule({ schedule: editable });
Defensive patterns

Strategy: validation

Validate before calling

if ('rule' in schedule) {
  const { rule, ...editable } = schedule;
  schedule = editable;
}
await updateSchedule({ schedule });

Type guard

function isUpdatableSchedule(s) {
  return s != null && typeof s.id === 'string' && !('rule' in s);
}

Try / catch

try {
  await updateSchedule({ schedule });
} catch (e) {
  if (e.message === 'You cannot change the rule of a schedule') {
    logger.warn('Stripped read-only rule field and retried');
    const { rule, ...rest } = schedule;
    await updateSchedule({ schedule: rest });
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling `updateSchedule({ id, rule: 'some-rule-id', ... })` with a `rule` property present on the schedule object — commonly from spreading a full ScheduleEntity (which includes `rule`) into the update payload.

Common situations: API clients that fetch a schedule and pass the whole object back into updateSchedule without deleting the read-only `rule` field.

Related errors


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