actualbudget/actual · error
Invalid tag name
Error message
Invalid tag name
What it means
renameTag() validates the new tag name before touching the database: after trimming, the name must match /^[^#\s]+$/ (at least one character, no whitespace, no leading '#'). Empty names, names containing spaces, or names containing '#' throw this plain Error('Invalid tag name').
Source
Thrown at packages/loot-core/src/server/tags/app.ts:122
async function updateTag(
tag: Partial<TagEntity> & Pick<TagEntity, 'id'>,
): Promise<Partial<TagEntity>> {
const { hidden, ...rest } = tag;
await db.updateTag({
...rest,
...(hidden !== undefined ? { hidden: hidden ? 1 : 0 } : {}),
});
return tag;
}
async function renameTag({
id,
tag: newTag,
}: Pick<TagEntity, 'id' | 'tag'>): Promise<TagEntity['id']> {
const name = newTag.trim();
// accept any char except whitespaces and '#', same as tag creation
if (!/^[^#\s]+$/.test(name)) {
throw new Error('Invalid tag name');
}
const tags = await db.getTags();
const allTags = await db.getAllTags();
const existing = tags.find(t => t.id === id);
if (!existing) {
throw new Error('Tag not found');
}
if (existing.tag === name) {
return id;
}
if (allTags.some(t => t.id !== id && t.tag === name)) {
throw new Error('A tag with that name already exists');
}
await batchMessages(async () => {
await db.updateTag({ id, tag: name });
View on GitHub (pinned to d4334cb6e6)
Solutions
- Trim the input and reject names that are empty, contain whitespace, or contain '#' before calling renameTag
- Strip a leading '#' from user-entered tag strings in the UI before validating
- Show inline validation matching the same regex so the user never submits an invalid name
Example fix
// before
await renameTag({ id, tag: '#groceries ' }); // Error: Invalid tag name
// after
const name = raw.replace(/^#+/, '').trim();
if (!/^[^#\s]+$/.test(name)) throw new Error('Invalid tag name');
await renameTag({ id, tag: name }); Defensive patterns
Strategy: validation
Validate before calling
function isValidTagName(name: string): boolean {
return /^[^#\s]+$/.test(name.trim());
} Type guard
function isInvalidTagNameError(e: unknown): boolean {
return e instanceof Error && e.message === 'Invalid tag name';
} Try / catch
try {
await renameTag({ id, tag });
} catch (e) {
if (isInvalidTagNameError(e)) {
notifyUser('Tags cannot contain spaces or # and cannot be empty');
} else throw e;
} Prevention
- Strip leading '#' and trim input in the UI before validating
- Reuse the same regex as the library for client-side checks
- Reject empty/whitespace-only names early
- Document the naming rule next to the input field
When it happens
Trigger: Calling renameTag({ id, tag: newTag }) with a tag that trims to empty string, contains whitespace (e.g. 'my tag'), or contains '#' (e.g. '#food') — the same rule as tag creation.
Common situations: Passing a UI string that still includes the '#' prefix; copying a tag with a trailing newline or spaces that trim to ''; programmatic renames that don't pre-sanitize user input.
Related errors
- A tag with that name already exists
- Invalid --name: must be a non-empty string.
- No update fields provided. Use --name or --offbudget.
- Invalid cutoff date: expected a valid date (e.g. YYYY-MM-DD)
- No update fields provided. Use --name or --hidden.
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/de19ea71454c734b.
Report an issue: GitHub.