actualbudget/actual · error
Total embedded font data exceeds maximum of ${MAX_TOTAL_FONT
Error message
Total embedded font data exceeds maximum of ${MAX_TOTAL_FONT_SIZE / 1024 / 1024}MB. What it means
validateFontFaceBlocks sums the estimated decoded size of all embedded base64 fonts in the theme and throws if the total exceeds MAX_TOTAL_FONT_SIZE (limit in MB shown in the message). This caps the overall payload a custom theme can carry.
Source
Thrown at packages/desktop-client/src/style/customThemes.ts:293
'Font files are automatically embedded when installing from GitHub.',
);
}
// Estimate decoded size from base64 content
const base64Match = uri.match(/;base64,(.+)$/);
if (base64Match) {
const size = Math.ceil((base64Match[1].length * 3) / 4);
if (size > MAX_FONT_FILE_SIZE) {
throw new Error(
`Font file exceeds maximum size of ${MAX_FONT_FILE_SIZE / 1024 / 1024}MB.`,
);
}
totalSize += size;
}
}
}
if (totalSize > MAX_TOTAL_FONT_SIZE) {
throw new Error(
`Total embedded font data exceeds maximum of ${MAX_TOTAL_FONT_SIZE / 1024 / 1024}MB.`,
);
}
}
/**
* Split CSS declarations by semicolons, but respect quoted strings and url() contents.
* This is needed because data: URIs contain semicolons (e.g., "data:font/woff2;base64,...").
*/
function splitDeclarations(content: string): string[] {
const declarations: string[] = [];
let start = 0;
let inSingleQuote = false;
let inDoubleQuote = false;
let parenDepth = 0;
for (let i = 0; i < content.length; i++) {
const ch = content[i];View on GitHub (pinned to d4334cb6e6)
Solutions
- Reduce the number of embedded weights/styles to only those actually used in the theme.
- Subset and WOFF2-compress each embedded font to shrink the total.
- Reference locally installed fonts via --font-* variables instead of embedding every family.
Example fix
// before: 6 @font-face blocks (4 weights x 2 families) embedded // after: keep only 2 @font-face blocks (regular + bold of one family), subset to WOFF2
Defensive patterns
Strategy: validation
Validate before calling
function checkTotalFontSize(css, maxBytes) {
let total = 0;
for (const m of css.matchAll(/;base64,([A-Za-z0-9+/=]+)/g)) {
total += Math.ceil(m[1].length * 3 / 4);
}
return total <= maxBytes;
} Try / catch
try {
await installTheme(css);
} catch (err) {
if ((err as Error).message.includes('Total embedded font data exceeds')) {
// reduce embedded font count/weights in the theme
} else throw err;
} Prevention
- Limit themes to a handful of embedded weights (regular + bold).
- Budget total font size across all families when designing a theme.
- Automate a size check over all data URIs in the theme build.
When it happens
Trigger: Installing a theme whose actual.css embeds several @font-face data URIs whose combined decoded size exceeds MAX_TOTAL_FONT_SIZE — even if each individual font is under its own limit.
Common situations: Themes bundling many font weights/styles (regular, bold, italic, multiple families) all embedded as base64; combining several large CJK/variable fonts in one theme.
Related errors
- Font file exceeds maximum size of ${MAX_FONT_FILE_SIZE / 102
- Invalid font src: only data: URIs are allowed in @font-face.
- Invalid font-family value for "${property}": value must not
- Invalid font-family value for "${property}": empty font name
- Invalid font-family value for "${property}": function calls
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/e500358649926277.
Report an issue: GitHub.