{"record":{"id":"98e807fd763d2b36","repo":"actualbudget/actual","slug":"invalid-font-family-value-for-property-value","errorCode":null,"errorMessage":"Invalid font-family value for \"${property}\": value must not be empty.","messagePattern":"Invalid font-family value for \"(.+?)\": value must not be empty\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/style/customThemes.ts","lineNumber":115,"sourceCode":" * Validate a font-family value for a --font-* CSS variable.\n *\n * Any font name is allowed — referencing a font the user doesn't have\n * installed simply triggers the browser's normal fallback behaviour\n * (no network requests). The only things we block are function calls\n * (url(), expression(), etc.) because those could load external resources\n * or execute expressions.\n *\n * Quoted or unquoted font names are both accepted.\n *\n * Examples of accepted values:\n *   Georgia, serif\n *   '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)) {","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/style/customThemes.ts#L97-L133","documentation":"validateFontFamilyValue checks every --font-* CSS variable value in a custom theme before it is installed. It throws this error when the entire font-family value is empty or whitespace-only. Font stacks must name at least one font (quoted or unquoted); an empty value would produce invalid CSS.","triggerScenarios":"A theme's actual.css contains a declaration like `--font-ui: ;` or `--font-mono:   ;` inside :root, which is passed through validatePropertyValue -> validateFontFamilyValue during validateThemeCss.","commonSituations":"Theme author left a font variable blank in their CSS template; a generator or minifier stripped the value; manual editing of actual.css accidentally deleted the font list.","solutions":["Fix the theme CSS: give the variable a value, e.g. `--font-ui: Georgia, serif;`.","Include at least one generic family fallback (serif, sans-serif, monospace).","Reinstall the theme after fixing the source actual.css."],"exampleFix":"// before (in actual.css)\n--font-ui: ;\n// after\n--font-ui: 'Inter', sans-serif;","handlingStrategy":"validation","validationCode":"function isValidFontValue(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}\nif (!isValidFontValue(rawDecl.value)) throw new Error('font value required');","typeGuard":"function isNonEmptyString(v: unknown): v is string {\n  return typeof v === 'string' && v.trim() !== '';\n}","tryCatchPattern":"try {\n  await installTheme(css);\n} catch (err) {\n  if ((err as Error).message.includes('value must not be empty')) {\n    // show which --font-* variable is blank in the theme editor\n  } else throw err;\n}","preventionTips":["Always end font stacks with a generic family (serif, sans-serif, monospace).","Lint theme CSS for empty declarations before publishing.","Never leave --font-* variables blank in templates."],"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"}