{"record":{"id":"1436e322028bc2cb","repo":"withastro/astro","slug":"cannotextractfonttype","errorCode":"CannotExtractFontType","errorMessage":"An error occurred while trying to extract the font type from ${url}.","messagePattern":"An error occurred while trying to extract the font type from (.+?)\\.","errorType":"exception","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/assets/fonts/infra/node-font-type-extractor.ts","lineNumber":11,"sourceCode":"import { extname } from 'node:path';\nimport { AstroError, AstroErrorData } from '../../../core/errors/index.js';\nimport type { FontTypeExtractor } from '../definitions.js';\nimport type { FontType } from '../types.js';\nimport { isFontType } from '../utils.js';\n\nexport class NodeFontTypeExtractor implements FontTypeExtractor {\n\textract(url: string): FontType {\n\t\tconst extension = extname(url).slice(1);\n\t\tif (!isFontType(extension)) {\n\t\t\tthrow new AstroError(\n\t\t\t\t{\n\t\t\t\t\t...AstroErrorData.CannotExtractFontType,\n\t\t\t\t\tmessage: AstroErrorData.CannotExtractFontType.message(url),\n\t\t\t\t},\n\t\t\t\t{ cause: `Unexpected extension, got \"${extension}\"` },\n\t\t\t);\n\t\t}\n\t\treturn extension;\n\t}\n}\n","sourceCodeStart":1,"sourceCodeEnd":22,"githubUrl":"https://github.com/withastro/astro/blob/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/astro/src/assets/fonts/infra/node-font-type-extractor.ts#L1-L22","documentation":"The fonts pipeline determines each font's type purely from the URL's file extension via `extname(url).slice(1)` and validates it against the supported set (`woff2`, `woff`, `otf`, `ttf`, `eot` — packages/astro/src/assets/fonts/constants.ts:27). An extension that is missing or not in that set throws `CannotExtractFontType` (packages/astro/src/assets/fonts/infra/node-font-type-extractor.ts:11).","triggerScenarios":"A font URL with no extension before the query string (`https://cdn.example.com/font?id=123`), an unsupported extension (`.ttc`, `.dfont`), or a path where the extension is swallowed by query/fragment placement.","commonSituations":"CDN URLs keyed by id instead of filename; renamed files losing their extension; using collection formats (.ttc) that the experimental Fonts API does not support.","solutions":["Point `url` at a file or URL whose extension is one of: woff2, woff, otf, ttf, eot (a query string after the extension is fine: `font.woff2?v=2` works, `font?v=2` does not).","For extension-less CDN URLs, download the font into the repo with a properly named file and use that.","If you control the server, rewrite the font route so URLs end with the real extension."],"exampleFix":"// before — extension-less CDN URL\nurl: 'https://cdn.example.com/fonts/123abc'\n\n// after — URL ends with a supported extension\nurl: 'https://cdn.example.com/fonts/123abc.woff2'","handlingStrategy":"validation","validationCode":"import { extname } from 'node:path';\nconst FONT_TYPES = ['woff2', 'woff', 'otf', 'ttf', 'eot'];\n// run against every font url before build\nfor (const url of fontUrls) {\n  const ext = extname(new URL(url, 'file://localhost/').pathname).slice(1);\n  if (!FONT_TYPES.includes(ext)) {\n    throw new Error(`Font URL must end in one of ${FONT_TYPES.join(', ')}: ${url}`);\n  }\n}","typeGuard":"const hasSupportedFontExtension = (url: string): boolean =>\n  ['woff2', 'woff', 'otf', 'ttf', 'eot'].includes(url.split('?')[0].split('.').pop() ?? '');","tryCatchPattern":null,"preventionTips":["Name all font files with their real extension.","Avoid id-keyed CDN URLs for fonts; use ones ending in the extension.","Add an extension lint for the assets/fonts directory."],"tags":["fonts","file-extension","fonts-api"],"backgroundTag":"unsupported-file-extension","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"}