{"record":{"id":"fd2db7da02a72fd8","repo":"withastro/astro","slug":"cannotfetchfontfile","errorCode":"CannotFetchFontFile","errorMessage":"An error occurred while fetching the font file from ${url}.","messagePattern":"An error occurred while fetching the font file from (.+?)\\.","errorType":"exception","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/assets/fonts/infra/cached-font-fetcher.ts","lineNumber":47,"sourceCode":"\t\t}\n\t\tconst data = await cb();\n\t\tawait storage.setItemRaw(key, data);\n\t\treturn data;\n\t}\n\n\tasync fetch({ id, url, init }: FontFileData): Promise<Buffer> {\n\t\treturn await this.#cache(this.#storage, id, async () => {\n\t\t\ttry {\n\t\t\t\tif (isAbsolute(url)) {\n\t\t\t\t\treturn await this.#readFile(url);\n\t\t\t\t}\n\t\t\t\tconst response = await this.#fetch(url, init ?? undefined);\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`Response was not successful, received status code ${response.status}`);\n\t\t\t\t}\n\t\t\t\treturn Buffer.from(await response.arrayBuffer());\n\t\t\t} catch (cause) {\n\t\t\t\tthrow new AstroError(\n\t\t\t\t\t{\n\t\t\t\t\t\t...AstroErrorData.CannotFetchFontFile,\n\t\t\t\t\t\tmessage: AstroErrorData.CannotFetchFontFile.message(url),\n\t\t\t\t\t},\n\t\t\t\t\t{ cause },\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n}\n","sourceCodeStart":29,"sourceCodeEnd":58,"githubUrl":"https://github.com/withastro/astro/blob/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/astro/src/assets/fonts/infra/cached-font-fetcher.ts#L29-L58","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["From the same machine/CI that builds, verify reachability: `curl -I <font-url>` and check for 200.","For absolute local paths, confirm the file exists and is readable at build time (`fs.existsSync` + permissions).","If the host blocks automated fetches, download the font into the repo and reference the local file instead.","Configure the proxy env vars (HTTP_PROXY/HTTPS_PROXY) your CI requires for outbound requests."],"exampleFix":"// astro.config.mjs — before (unreachable / hotlink-protected URL)\nurl: 'https://fonts.example.com/files/my-font.woff2'\n\n// after — vendored local file\nurl: './src/assets/fonts/my-font.woff2'","handlingStrategy":"retry","validationCode":"// preflight every remote font before build (run in CI before astro build)\nconst results = await Promise.all(fontUrls.map(async (url) => {\n  try {\n    const res = await fetch(url, { method: 'HEAD' });\n    return { url, ok: res.ok, status: res.status };\n  } catch (e) {\n    return { url, ok: false, status: String(e) };\n  }\n}));\nconst bad = results.filter((r) => !r.ok);\nif (bad.length) throw new Error('Unreachable fonts: ' + JSON.stringify(bad, null, 2));","typeGuard":null,"tryCatchPattern":"try {\n  await fontFetcher.fetch({ id, url });\n} catch (err) {\n  if (err.name === 'CannotFetchFontFile') {\n    // transient network: wait and retry once; permanent (404): surface the URL and fail the build\n  } else throw err;\n}","preventionTips":["Vendor critical fonts into the repo instead of fetching from CDNs at build time.","Add a CI preflight that HEAD-checks every configured font URL.","Pin font CDN URLs with stable, versioned paths to avoid link rot."],"tags":["fonts","network","build","ci"],"backgroundTag":"remote-asset-fetch-failed","analyzedSha":"52e6c34790cc8ac4e69e6135ace06049867e5c4a","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}