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
- Ensure the item passed to updateReport was loaded from the database and retains its id
- Generate/preserve an id before calling updateReport (e.g. via the report creation path) and pass it through
- 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
- Always load the report via a getter before updating instead of constructing payloads from scratch
- Validate the id presence at the UI/form boundary before calling mutation APIs
- Keep create vs update code paths clearly separated
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
- Invalid end date format
- Invalid date format provided
- Invalid date values provided
- Start date must be before or equal to end date.
- Invalid --name: must be a non-empty string.
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/2ec2d6dcbc12eb6e.
Report an issue: GitHub.