{"record":{"id":"5a5288e88926b8b3","repo":"actualbudget/actual","slug":"invalid-property-property-property-name-cann-5a5288","errorCode":null,"errorMessage":"Invalid property \"${property}\". Property name cannot end with a dash.","messagePattern":"Invalid property \"(.+?)\"\\. Property name cannot end with a dash\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/style/customThemes.ts","lineNumber":407,"sourceCode":"    // Check for invalid characters in property name (no brackets, spaces, special chars except dash/underscore)\n    // Property name after -- should only contain: letters, digits, underscore, and dashes (not consecutive dashes at start/end)\n    const propertyNameAfterDashes = property.substring(2);\n    if (propertyNameAfterDashes.length === 0) {\n      throw new Error(\n        `Invalid property \"${property}\". Property name cannot be empty after \"--\".`,\n      );\n    }\n\n    // Check for invalid characters (no brackets, no special characters except underscore and dash)\n    if (!/^[a-zA-Z0-9_-]+$/.test(propertyNameAfterDashes)) {\n      throw new Error(\n        `Invalid property \"${property}\". Property name contains invalid characters. Only letters, digits, underscores, and dashes are allowed.`,\n      );\n    }\n\n    // Check that property doesn't end with a dash (after the -- prefix)\n    if (property.endsWith('-')) {\n      throw new Error(\n        `Invalid property \"${property}\". Property name cannot end with a dash.`,\n      );\n    }\n\n    // Extract and validate the value\n    const value = decl.substring(colonIndex + 1).trim();\n    validatePropertyValue(value, property);\n  }\n}\n\n// ─── Main validation entry point ────────────────────────────────────────────\n\n/**\n * Validate theme CSS. Accepts:\n * 1. Optional @font-face blocks (with data: URI fonts only)\n * 2. Exactly one :root { ... } block with CSS variable declarations\n *\n * @font-face blocks must appear before :root.","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/style/customThemes.ts#L389-L425","documentation":"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.","triggerScenarios":"Calling validateThemeCss with ':root { --color-: red; }', or names built by concatenation like `--color-${variant}` where variant is empty, leaving a dangling dash.","commonSituations":"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.","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"],"exampleFix":"// before\n:root {\n  --color-: red;\n}\n// after\n:root {\n  --color-accent: red;\n}","handlingStrategy":"validation","validationCode":"function noTrailingDash(css: string): boolean {\n  const root = css.match(/:root\\s*\\{([\\s\\S]*?)\\}/);\n  if (!root) return false;\n  return root[1]\n    .split(';')\n    .map((d) => d.trim())\n    .filter(Boolean)\n    .every((d) => {\n      const name = d.split(':')[0].trim();\n      return !name.endsWith('-');\n    });\n}","typeGuard":"const nameIsComplete = (prop: string): boolean => !prop.replace(/\\s*:.*/, '').trim().endsWith('-');","tryCatchPattern":"try {\n  const validated = validateThemeCss(userCss);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('end with a dash')) {\n    showError('A variable name ends with a dash — complete or remove it.');\n  }\n}","preventionTips":["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"],"tags":["css","validation","custom-themes","naming"],"backgroundTag":"invalid-css-custom-property","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}