{"record":{"id":"924cb7999d4f7e91","repo":"actualbudget/actual","slug":"invalid-property-property-property-name-cont","errorCode":null,"errorMessage":"Invalid property \"${property}\". Property name contains invalid characters. Only letters, digits, underscores, and dashes are allowed.","messagePattern":"Invalid property \"(.+?)\"\\. Property name contains invalid characters\\. Only letters, digits, underscores, and dashes are allowed\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/style/customThemes.ts","lineNumber":400,"sourceCode":"    // - Contain only valid characters (letters, digits, underscore, dash, but not at start/end positions)\n    if (property === '--' || property === '-') {\n      throw new Error(\n        `Invalid property \"${property}\". Property name cannot be empty or contain only dashes.`,\n      );\n    }\n\n    // 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 ────────────────────────────────────────────","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/style/customThemes.ts#L382-L418","documentation":"Custom property names in theme CSS may only contain letters, digits, underscores, and dashes after the '--' prefix. validateRootContent enforces this with the regex /^[a-zA-Z0-9_-]+$/ and rejects names containing spaces, brackets, dots, emoji, or other special characters, closing a CSS-injection surface.","triggerScenarios":"Calling validateThemeCss with a property like '--color[main]: red;', '--my var: x;', '--font.size: 12px;', or any name with escaped/unicode/special characters inside :root.","commonSituations":"Copy-pasted CSS using hyphenated names with spaces, generated names containing dots or brackets, or themes ported from systems that allowed arbitrary variable names.","solutions":["Rename the property using only letters, digits, underscores, and dashes, e.g. '--my var' -> '--my-var'","Escape-free rewrite: replace '.', '[', ']', spaces, etc. with '-' or '_' in the variable name","Check the code that generates variable names and restrict it to /^[a-zA-Z0-9_-]+$/","Remove the offending declaration if it is not needed"],"exampleFix":"// before\n:root {\n  --color[main]: red;\n}\n// after\n:root {\n  --color-main: red;\n}","handlingStrategy":"validation","validationCode":"const CUSTOM_PROP_NAME = /^--[a-zA-Z0-9_-]+[a-zA-Z0-9_]$/;\nfunction namesAreSafe(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) => CUSTOM_PROP_NAME.test(d.split(':')[0].trim()));\n}","typeGuard":"const isValidPropName = (prop: string): boolean => /^--[a-zA-Z0-9_-]+$/.test(prop.substring(0, prop.indexOf(':')).trim());","tryCatchPattern":"try {\n  const validated = validateThemeCss(userCss);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('invalid characters')) {\n    showError('Variable names may only use letters, digits, underscores, and dashes.');\n  }\n}","preventionTips":["Slugify generated names to [a-zA-Z0-9_-] before use","Replace spaces/dots/brackets in pasted names with dashes","Reject non-ASCII and special characters in a pre-save check","Keep a variable-name linter in the theme editor"],"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"}