actualbudget/actual · error · Error
There is already a report named ${item.name}
Error message
There is already a report named ${item.name} What it means
Thrown by createReport when reportNameExists finds an existing non-deleted (tombstone = 0) custom_reports row with the same name. Names act as unique identifiers for custom reports among living reports, so creating a duplicate name is rejected with this message.
Source
Thrown at packages/loot-core/src/server/reports/app.ts:146
}
//default return: item was found but does not match current name
return true;
}
async function createReport(report: CustomReportEntity) {
const reportId = uuidv4();
const item: CustomReportEntity = {
...report,
id: reportId,
};
if (!item.name) {
throw new Error('Report name is required');
}
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);View on GitHub (pinned to d4334cb6e6)
Solutions
- Pick a unique name — check existing names first via getReports and suffix if taken
- Wrap the call and catch the error, then fall back to creating with a suffixed name ('Name (2)')
- For re-runnable imports, look up the existing report by name and update it (updateReport) instead of creating
- Delete the existing duplicate report if it is stale and recreation is intended
Example fix
// before
await app.createReport({ name: existingName, ...report });
// after
const existing = reports.find(r => r.name === report.name);
if (existing) {
await app.updateReport({ ...report, id: existing.id });
} else {
await app.createReport(report);
} Defensive patterns
Strategy: try-catch
Validate before calling
const existing = await app.getReports();
if (existing.some(r => r.name === newReport.name)) {
newReport.name = `${newReport.name} (${new Date().toISOString().slice(0, 10)})`;
} Try / catch
try {
await app.createReport(report);
} catch (err) {
if (err instanceof Error && err.message.startsWith('There is already a report named ')) {
await app.createReport({ ...report, name: report.name + ' (2)' });
} else {
throw err;
}
} Prevention
- Look up existing reports via getReports and de-duplicate names before creating
- Make import scripts idempotent: update by name instead of blindly creating
- Suffix generated names with a timestamp or uuid in automation
- Reuse reportNameExists-equivalent checks in caller code when batch-inserting
When it happens
Trigger: Calling createReport with a name that matches any existing report in the custom_reports table (case-exact match via SQL name = ?), regardless of the new report's id — e.g. re-running an import script, duplicating a report through the API, or retrying a create after a partial failure.
Common situations: Idempotency-lacking automation that re-imports reports on every run; users/API scripts recreating a report that already exists; testing scripts that insert the same sample report repeatedly; renaming one report to collide with another before create.
Related errors
- Only one `up to` allowed per category
- Only one spend template is allowed per category
- Only one #goal is allowed per category
- Category '${category.name}' already exists in group '${categ
- An '${existingGroup.name}' account group already exists.
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/97ffffbf5f305b45.
Report an issue: GitHub.