actualbudget/actual · error
Invalid font-family value for "${property}": function calls
Error message
Invalid font-family value for "${property}": function calls are not allowed. Only font names are permitted. What it means
validateFontFamilyValue rejects any font name containing an opening parenthesis, because function calls like url(), var(), or expression() in font-family could load external resources or execute expressions. Only plain font names (quoted or unquoted) and generic families are permitted.
Source
Thrown at packages/desktop-client/src/style/customThemes.ts:134
`Invalid font-family value for "${property}": value must not be empty.`,
);
}
// Split on commas, then validate each font name
const families = trimmed.split(',');
for (const raw of families) {
const name = stripQuotes(raw);
if (!name) {
throw new Error(
`Invalid font-family value for "${property}": empty font name in comma-separated list.`,
);
}
// Reject anything that looks like a function call (url(), expression(), etc.)
if (/\(/.test(name)) {
throw new Error(
`Invalid font-family value for "${property}": function calls are not allowed. Only font names are permitted.`,
);
}
}
}
/** Only var(--custom-property-name) is allowed; no fallbacks. Variable name: -- then [a-zA-Z0-9_-]+ (no trailing dash). */
const VAR_ONLY_PATTERN = /^var\s*\(\s*(--[a-zA-Z0-9_-]+)\s*\)$/i;
function isValidSimpleVarValue(value: string): boolean {
const m = value.trim().match(VAR_ONLY_PATTERN);
if (!m) return false;
const name = m[1];
return name !== '--' && !name.endsWith('-');
}
/**
* Validate that a CSS property value only contains allowed content (allowlist approach).View on GitHub (pinned to d4334cb6e6)
Solutions
- Replace the function call with a plain font-name list, e.g. `--font-ui: Inter, sans-serif;`.
- Remove any url(...) from font-family — remote fonts are intentionally blocked for privacy; use @font-face with data: URIs if embedding is needed.
- Do not nest var() inside --font-* values; put the final font list directly in the variable.
Example fix
// before --font-ui: url(https://cdn.example.com/fonts/inter.woff2); // after --font-ui: Inter, sans-serif;
Defensive patterns
Strategy: validation
Validate before calling
function fontValueIsSafe(v) {
return !v.split(',').some(part => part.replace(/^["']|["']$/g, '').trim().includes('('));
}
if (!fontValueIsSafe(value)) throw new Error('font-family must not contain function calls'); Type guard
function isPlainFontList(v: string): boolean {
return /^[^()]+$/.test(v);
} Try / catch
try {
await installTheme(css);
} catch (err) {
if ((err as Error).message.includes('function calls are not allowed')) {
// reject theme or ask author to supply a plain font-name list
} else throw err;
} Prevention
- Never use url() or var() inside --font-* values.
- Embed remote fonts (if at all) via data-URI @font-face, not via font-family.
- Review third-party theme CSS for parentheses before installing.
When it happens
Trigger: A --font-* declaration whose value contains a function call, e.g. `--font-ui: url(https://evil.example/font.woff2);` or `--font-mono: var(--other-font);`, anywhere in the theme's :root block.
Common situations: Malicious or careless theme CSS attempting to load remote fonts via url() inside font-family; theme authors misusing var() inside a font stack instead of declaring a plain list.
Related errors
- Invalid font-family value for "${property}": value must not
- Invalid font-family value for "${property}": empty font name
- Invalid font src: only data: URIs are allowed in @font-face.
- Theme CSS contains forbidden at-rules (@import, @media, @key
- Invalid value "${trimmedValue}" for property "${property}".
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/442e18b09f446cd5.
Report an issue: GitHub.