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
- Remove the `rule` property from the object passed to updateSchedule
- Update rule behavior via the `conditions` parameter or by editing the rule itself
- 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
- Never include the `rule` property in updateSchedule payloads
- Destructure/pick only mutable fields before sending updates
- Treat rule changes as create-new-schedule + delete-old, not an update
- Type update payloads as Omit<ScheduleEntity, 'rule'> where possible
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
- Schedule ${t.name ?? t.scheduleId} does not exist
- Schedule ${t.name.trim()} does not exist
- Schedule template has no scheduleId or name
- Schedule and By templates must be the same priority level. F
- parse-recur-date
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/ffb87a0064cd74b1.
Report an issue: GitHub.