{"record":{"id":"554c1d682116b5d9","repo":"actualbudget/actual","slug":"invalid-font-family-value-for-property-empty","errorCode":null,"errorMessage":"Invalid font-family value for \"${property}\": empty font name in comma-separated list.","messagePattern":"Invalid font-family value for \"(.+?)\": empty font name in comma-separated list\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/style/customThemes.ts","lineNumber":127,"sourceCode":" *   'Fira Code', monospace\n *   \"My Theme Font\", sans-serif\n */\nfunction validateFontFamilyValue(value: string, property: string): void {\n  const trimmed = value.trim();\n  if (!trimmed) {\n    throw new Error(\n      `Invalid font-family value for \"${property}\": value must not be empty.`,\n    );\n  }\n\n  // Split on commas, then validate each font name\n  const families = trimmed.split(',');\n\n  for (const raw of families) {\n    const name = stripQuotes(raw);\n\n    if (!name) {\n      throw new Error(\n        `Invalid font-family value for \"${property}\": empty font name in comma-separated list.`,\n      );\n    }\n\n    // Reject anything that looks like a function call (url(), expression(), etc.)\n    if (/\\(/.test(name)) {\n      throw new Error(\n        `Invalid font-family value for \"${property}\": function calls are not allowed. Only font names are permitted.`,\n      );\n    }\n  }\n}\n\n/** Only var(--custom-property-name) is allowed; no fallbacks. Variable name: -- then [a-zA-Z0-9_-]+ (no trailing dash). */\nconst VAR_ONLY_PATTERN = /^var\\s*\\(\\s*(--[a-zA-Z0-9_-]+)\\s*\\)$/i;\n\nfunction isValidSimpleVarValue(value: string): boolean {\n  const m = value.trim().match(VAR_ONLY_PATTERN);","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/style/customThemes.ts#L109-L145","documentation":"validateFontFamilyValue splits the font-family value on commas and validates each name after stripping quotes. This error is thrown when one of the comma-separated entries is empty (e.g. a trailing comma or consecutive commas), meaning the font stack names no font at that position.","triggerScenarios":"A --font-* variable value like `--font-ui: Inter,, sans-serif;` or `--font-mono: 'Fira Code', ;` in the theme's actual.css — any entry that is empty after quote stripping triggers the throw.","commonSituations":"Theme author left a dangling comma after editing the font stack; string concatenation or templating in a theme generator produced ',,'; hand-editing removed a font name but not its comma.","solutions":["Remove the empty entry or dangling comma in the font stack.","Ensure every comma-separated item names a font or a generic family.","Reinstall the theme after correcting actual.css."],"exampleFix":"// before\n--font-ui: Inter, , sans-serif;\n// after\n--font-ui: Inter, sans-serif;","handlingStrategy":"validation","validationCode":"function hasNoEmptyFontEntries(v) {\n  return v.split(',').every(part => part.replace(/^[\"']|[\"']$/g, '').trim() !== '');\n}\nif (!hasNoEmptyFontEntries(value)) throw new Error('dangling comma in font stack');","typeGuard":"function isWellFormedFontList(v: string): boolean {\n  return v.trim().split(',').every(p => p.replace(/['\"]/g, '').trim() !== '');\n}","tryCatchPattern":"try {\n  await installTheme(css);\n} catch (err) {\n  if ((err as Error).message.includes('empty font name')) {\n    // highlight the malformed font stack for the theme author\n  } else throw err;\n}","preventionTips":["Avoid hand-editing comma-separated font stacks; use a linter that flags dangling commas.","Test-render the font stack in a browser before publishing the theme.","Generate font lists programmatically from a fixed array joined with ', '."],"tags":["css","validation","font-family","theme"],"backgroundTag":"css-value-validation-failed","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}