actualbudget/actual · error · Error

Report recall error

Error message

Report recall error

What it means

updateReport requires a valid custom report id to update the existing row in the custom_reports table. When item.id is falsy (missing, null, or empty string), the function cannot recall which report to update, so it throws 'Report recall error' before any database work happens.

Source

Thrown at packages/loot-core/src/server/reports/app.ts:161

  const nameExists = await reportNameExists(item.name, item.id ?? '', true);
  if (nameExists) {
    throw new Error('There is already a report named ' + item.name);
  }

  // Create the report here based on the info
  await db.insertWithSchema('custom_reports', reportModel.fromJS(item));

  return reportId;
}

async function updateReport(item: CustomReportEntity) {
  if (!item.name) {
    throw new Error('Report name is required');
  }

  if (!item.id) {
    throw new Error('Report recall error');
  }

  const nameExists = await reportNameExists(item.name, item.id, false);
  if (nameExists) {
    throw new Error('There is already a report named ' + item.name);
  }

  await db.updateWithSchema('custom_reports', reportModel.fromJS(item));
}

async function deleteReport(id: CustomReportEntity['id']) {
  await db.delete_('custom_reports', id);
}

export type ReportsHandlers = {
  'report/get': typeof getReports;
  'report/create': typeof createReport;
  'report/update': typeof updateReport;

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Ensure the item passed to updateReport was loaded from the database and retains its id
  2. Generate/preserve an id before calling updateReport (e.g. via the report creation path) and pass it through
  3. Distinguish create vs update flows: call createReport for new reports, updateReport only for existing ones

Example fix

// before
await updateReport({ name: 'Groceries', groupId: 'g1' }); // no id
// after
const existing = await getReport(reportId);
await updateReport({ ...existing, name: 'Groceries' });
Defensive patterns

Strategy: validation

Validate before calling

if (!item.id) throw new Error('Cannot update a report without an id'); await updateReport(item);

Type guard

function hasId(report: CustomReportEntity): report is CustomReportEntity & { id: string } {
  return typeof report.id === 'string' && report.id.length > 0;
}

Try / catch

try {
  await updateReport(report);
} catch (e) {
  if (e instanceof Error && e.message === 'Report recall error') {
    // fall back to createReport or re-fetch the report
  } else throw e;
}

Prevention

When it happens

Trigger: Calling updateReport with a CustomReportEntity whose id field is undefined/null/empty — e.g. passing a newly constructed report object (as if creating) instead of a saved report, or deserializing a report where the id key was dropped.

Common situations: UI code building a report object from form state that never assigns the id; code reusing a createReport payload shape for updates; JSON round-trips that strip the id field.

Related errors


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