actualbudget/actual · error
Invalid property "${property}". Property name cannot end wit
Error message
Invalid property "${property}". Property name cannot end with a dash. What it means
A custom property name may not end with a dash. validateRootContent checks property.endsWith('-') after validating characters and rejects trailing-dash names (e.g. '--color-'). Such names are almost always truncation or typo artifacts and some CSS parsers treat them inconsistently.
Source
Thrown at packages/desktop-client/src/style/customThemes.ts:407
// Check for invalid characters in property name (no brackets, spaces, special chars except dash/underscore)
// Property name after -- should only contain: letters, digits, underscore, and dashes (not consecutive dashes at start/end)
const propertyNameAfterDashes = property.substring(2);
if (propertyNameAfterDashes.length === 0) {
throw new Error(
`Invalid property "${property}". Property name cannot be empty after "--".`,
);
}
// Check for invalid characters (no brackets, no special characters except underscore and dash)
if (!/^[a-zA-Z0-9_-]+$/.test(propertyNameAfterDashes)) {
throw new Error(
`Invalid property "${property}". Property name contains invalid characters. Only letters, digits, underscores, and dashes are allowed.`,
);
}
// Check that property doesn't end with a dash (after the -- prefix)
if (property.endsWith('-')) {
throw new Error(
`Invalid property "${property}". Property name cannot end with a dash.`,
);
}
// Extract and validate the value
const value = decl.substring(colonIndex + 1).trim();
validatePropertyValue(value, property);
}
}
// ─── Main validation entry point ────────────────────────────────────────────
/**
* Validate theme CSS. Accepts:
* 1. Optional @font-face blocks (with data: URI fonts only)
* 2. Exactly one :root { ... } block with CSS variable declarations
*
* @font-face blocks must appear before :root.View on GitHub (pinned to d4334cb6e6)
Solutions
- Complete or remove the trailing dash, e.g. '--color-' -> '--color-bg'
- Fix the name-building code so the interpolated suffix is always non-empty
- Delete the incomplete declaration
- Pre-validate with /^--[a-zA-Z0-9_-]*[a-zA-Z0-9_]$/ before calling validateThemeCss
Example fix
// before
:root {
--color-: red;
}
// after
:root {
--color-accent: red;
} Defensive patterns
Strategy: validation
Validate before calling
function noTrailingDash(css: string): boolean {
const root = css.match(/:root\s*\{([\s\S]*?)\}/);
if (!root) return false;
return root[1]
.split(';')
.map((d) => d.trim())
.filter(Boolean)
.every((d) => {
const name = d.split(':')[0].trim();
return !name.endsWith('-');
});
} Type guard
const nameIsComplete = (prop: string): boolean => !prop.replace(/\s*:.*/, '').trim().endsWith('-'); Try / catch
try {
const validated = validateThemeCss(userCss);
} catch (e) {
if (e instanceof Error && e.message.includes('end with a dash')) {
showError('A variable name ends with a dash — complete or remove it.');
}
} Prevention
- Check template suffixes are non-empty before interpolation
- Trim trailing dashes in any name-normalization step
- Pre-validate with /^--[a-zA-Z0-9_-]*[a-zA-Z0-9_]$/
- Review copy-pasted names for truncation
When it happens
Trigger: Calling validateThemeCss with ':root { --color-: red; }', or names built by concatenation like `--color-${variant}` where variant is empty, leaving a dangling dash.
Common situations: Template strings with an undefined/empty suffix variable, copy-paste that cut off the last character of a name, or manual editing that dropped the final part of a variable name.
Related errors
- Invalid property "${property}". Property name contains inval
- Invalid CSS declaration: "${decl}"
- Invalid property "${property}". Only CSS custom properties (
- Invalid property "${property}". Property name cannot be empt
- Invalid property "${property}". Property name cannot be empt
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/5a5288e88926b8b3.
Report an issue: GitHub.