withastro/astro · error · AstroError

CannotFetchFontFile

CannotFetchFontFile

Error message

An error occurred while fetching the font file from ${url}.

What it means

The fonts pipeline fetches every configured font file at build time through `cached-font-fetcher.ts`: absolute paths are read from disk with `readFile`, everything else with `fetch`, and a non-OK status is converted to an error. Any failure (network error, 404/403, DNS/TLS issue, ENOENT on a local path) is wrapped as `CannotFetchFontFile` with the original error as `cause` (packages/astro/src/assets/fonts/infra/cached-font-fetcher.ts:47).

Solutions

  1. From the same machine/CI that builds, verify reachability: `curl -I <font-url>` and check for 200.
  2. For absolute local paths, confirm the file exists and is readable at build time (`fs.existsSync` + permissions).
  3. If the host blocks automated fetches, download the font into the repo and reference the local file instead.
  4. Configure the proxy env vars (HTTP_PROXY/HTTPS_PROXY) your CI requires for outbound requests.

Example fix

// astro.config.mjs — before (unreachable / hotlink-protected URL)
url: 'https://fonts.example.com/files/my-font.woff2'

// after — vendored local file
url: './src/assets/fonts/my-font.woff2'
Defensive patterns

Strategy: retry

Validate before calling

// preflight every remote font before build (run in CI before astro build)
const results = await Promise.all(fontUrls.map(async (url) => {
  try {
    const res = await fetch(url, { method: 'HEAD' });
    return { url, ok: res.ok, status: res.status };
  } catch (e) {
    return { url, ok: false, status: String(e) };
  }
}));
const bad = results.filter((r) => !r.ok);
if (bad.length) throw new Error('Unreachable fonts: ' + JSON.stringify(bad, null, 2));

Try / catch

try {
  await fontFetcher.fetch({ id, url });
} catch (err) {
  if (err.name === 'CannotFetchFontFile') {
    // transient network: wait and retry once; permanent (404): surface the URL and fail the build
  } else throw err;
}

Prevention

When it happens

Trigger: A `url` in the `fonts` config that points to an unreachable remote host, returns 403/404 (hotlink protection, expired signed URL), or an absolute filesystem path that does not exist / is not readable on the build machine.

Common situations: CI builds without network access or behind a proxy; font CDNs blocking non-browser user-agents; Google Fonts URLs with expired parameters; local paths that exist on the dev machine but not in the CI container.

Related errors


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/fd2db7da02a72fd8. Report an issue: GitHub.

Appendix: source

Thrown at packages/astro/src/assets/fonts/infra/cached-font-fetcher.ts:47

		}
		const data = await cb();
		await storage.setItemRaw(key, data);
		return data;
	}

	async fetch({ id, url, init }: FontFileData): Promise<Buffer> {
		return await this.#cache(this.#storage, id, async () => {
			try {
				if (isAbsolute(url)) {
					return await this.#readFile(url);
				}
				const response = await this.#fetch(url, init ?? undefined);
				if (!response.ok) {
					throw new Error(`Response was not successful, received status code ${response.status}`);
				}
				return Buffer.from(await response.arrayBuffer());
			} catch (cause) {
				throw new AstroError(
					{
						...AstroErrorData.CannotFetchFontFile,
						message: AstroErrorData.CannotFetchFontFile.message(url),
					},
					{ cause },
				);
			}
		});
	}
}

View on GitHub (pinned to 52e6c34790)