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

  1. Complete or remove the trailing dash, e.g. '--color-' -> '--color-bg'
  2. Fix the name-building code so the interpolated suffix is always non-empty
  3. Delete the incomplete declaration
  4. 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

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


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