remotion-dev/remotion · error
Failed to load font
Error message
Failed to load font ${JSON.stringify(fontUrl)}: ${response.status} ${response.statusText} What it means
Remotion fetches @font-face font files at composition time so they can be embedded during rendering. If the HTTP response for the font URL is not ok (e.g. 404, 403, 500), fetchFontData throws with the URL and status. Rendering fails fast rather than silently falling back to a missing font.
Solutions
- Verify the font URL returns 200 (curl -I the URL).
- Fix or re-upload the font asset / regenerate presigned URLs.
- Use a local font file path or hosted URL that is reachable from the render environment.
- Check network/firewall and CORS settings in the rendering environment.
Example fix
// before
await loadFont({family: 'X', url: 'https://cdn.example.com/old-font.woff2'})
// after
await loadFont({family: 'X', url: 'https://cdn.example.com/font-v2.woff2'}) // verified 200 Defensive patterns
Strategy: retry
Validate before calling
const res = await fetch(fontUrl, {method: 'HEAD'});
if (!res.ok) throw new Error(`Font unavailable: ${res.status}`); Try / catch
try {
await loadFont({family, url: fontUrl});
} catch (e) {
if (String(e).includes('Failed to load font')) {
await loadFont({family, url: fallbackFontUrl});
}
} Prevention
- Pre-verify font URLs return 200 before rendering.
- Avoid short-lived presigned URLs in render environments.
- Self-host fonts next to the project or bundle them via staticFile().
- Check network egress in Lambda/CI.
When it happens
Trigger: Loading a font via loadFont/FontFace with a URL that returns a non-2xx status; expired signed URLs; CORS-proxied or blocked font hosts returning error pages.
Common situations: Typo'd font URL; font removed from CDN; S3/GCS presigned URL expired; local dev server not running when rendering; network restrictions in Lambda/CI.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Could not load LUT (HTTP ).
- Expected status code 200 or 404 for file, got
- Failed to fetch audio data from
- HTTP error, status =
- HTTP response of has no body
AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18).
Data as JSON: /api/errors/c8eaf09a8cf3c630.
Report an issue: GitHub.
Appendix: source
Thrown at packages/core/src/font-face-registry.ts:30
weight: string | null;
stretch: string | null;
unicodeRange: string | null;
variant: string | null;
};
const registeredFontFaces: RegisteredFontFace[] = [];
const fontDataByUrl = new Map<string, Promise<ArrayBuffer>>();
export const fetchFontData = (fontUrl: string): Promise<ArrayBuffer> => {
const cached = fontDataByUrl.get(fontUrl);
if (cached) {
return cached;
}
const promise = fetch(fontUrl)
.then((response) => {
if (!response.ok) {
throw new Error(
`Failed to load font ${JSON.stringify(fontUrl)}: ${response.status} ${response.statusText}`,
);
}
return response.arrayBuffer();
})
.catch((error) => {
fontDataByUrl.delete(fontUrl);
throw error;
});
fontDataByUrl.set(fontUrl, promise);
return promise;
};
export const registerFontFace = (fontFace: RegisteredFontFace) => {
if (
registeredFontFaces.some(View on GitHub (pinned to 46a3a6bf13)