actualbudget/actual · error · APIError
Schedule ${id} not found
Error message
Schedule ${id} not found What it means
api.updateSchedule() looks up the schedule row via an AQL query before mutating conditions. If no schedule with that id exists in the schedules table, the handler throws this APIError naming the missing id.
Source
Thrown at packages/loot-core/src/server/api.ts:952
const partialSchedule = {
name: internalSchedule.name,
posts_transaction: internalSchedule.posts_transaction,
};
return handlers['schedule/create']({
schedule: partialSchedule,
conditions: internalSchedule._conditions,
});
});
handlers['api/schedule-update'] = withMutation(async function ({
id,
fields,
resetNextDate,
}) {
checkFileOpen();
const { data } = await aqlQuery(q('schedules').filter({ id }).select('*'));
if (!data || data.length === 0) {
throw APIError(`Schedule ${id} not found`);
}
const sched = data[0] as ScheduleEntity;
let conditionsUpdated = false;
// Find all indices to avoid direct assignment
const payeeIndex = sched._conditions.findIndex(c => c.field === 'payee');
const accountIndex = sched._conditions.findIndex(c => c.field === 'account');
const dateIndex = sched._conditions.findIndex(c => c.field === 'date');
const amountIndex = sched._conditions.findIndex(c => c.field === 'amount');
for (const key in fields) {
const typedKey = key as keyof APIScheduleEntity;
const value = fields[typedKey];
switch (typedKey) {
case 'name': {
const newName = String(value);
const { data: existing } = await aqlQuery(View on GitHub (pinned to d4334cb6e6)
Solutions
- Verify the id by listing schedules and matching (via aqlQuery on q('schedules') or the UI)
- Re-fetch schedule ids after any budget import/reset — don't persist schedule ids across budget rebuilds
- Log the id being passed and confirm the producer of that id returns schedule entities
Example fix
// before
await api.updateSchedule(someRow.id, fields);
// after
const { data } = await api.aqlQuery(q('schedules').filter({ id: someRow.id }).select('*'));
if (data && data.length) await api.updateSchedule(someRow.id, fields); Defensive patterns
Strategy: validation
Validate before calling
const { data } = await aqlQuery(q('schedules').filter({ id }).select('*'));
if (!data || data.length === 0) throw APIError(`Schedule ${id} not found`); Type guard
function scheduleRowExists(res) {
return Array.isArray(res?.data) && res.data.length > 0;
} Try / catch
try {
await updateSchedule(id, fields);
} catch (e) {
if (e instanceof APIError && e.message.includes('not found')) {
return { ok: false, reason: 'missing-schedule' };
}
throw e;
} Prevention
- Resolve ids through the schedules table only
- Purge cached schedule ids after budget resets
- Return structured not-found results to callers
When it happens
Trigger: Calling api.updateSchedule(id, fields, resetNextDate) with an id that is not a schedule id (e.g. a transaction or account id), an id from a different budget, or an id of a schedule already deleted.
Common situations: Stale references after re-importing or recreating a budget; passing the payee id or transaction id instead of the schedule id; concurrent deletion of the schedule between fetch and update.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Schedule not found: ${id}
- ${debug}: category "${id}" does not exist
- There is already a schedule named: ${newName}
- Field ${typedKey} is system-managed and not user-editable.
- Invalid amount operator: ${String(value)}. Expected: is, isa
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/e9422a722997560e.
Report an issue: GitHub.