{"record":{"id":"2701749d7c526b4b","repo":"remix-run/remix","slug":"files-extensions-values-must-use-ext-format-re","errorCode":null,"errorMessage":"files.extensions values must use \".ext\" format. Received \"${extension}\".","messagePattern":"files\\.extensions values must use \"\\.ext\" format\\. Received \"(.+?)\"\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/assets/src/lib/files/config.ts","lineNumber":197,"sourceCode":"      transforms: new Map(),\n    }\n  }\n\n  if (!Array.isArray(files.extensions)) {\n    throw new TypeError('files.extensions must be an array')\n  }\n\n  let normalizedExtensions: string[] = []\n  let seen = new Set<string>()\n\n  for (let extension of files.extensions) {\n    if (typeof extension !== 'string') {\n      throw new TypeError('files.extensions values must be strings')\n    }\n\n    let normalizedExtension = extension.trim().toLowerCase()\n    if (!/^\\.[A-Za-z0-9_-]+$/.test(normalizedExtension)) {\n      throw new TypeError(\n        `files.extensions values must use \".ext\" format. Received \"${extension}\".`,\n      )\n    }\n\n    if (reservedFileExtensions.has(normalizedExtension)) {\n      throw new TypeError(\n        `files.extensions cannot include compiled asset extensions like \"${normalizedExtension}\".`,\n      )\n    }\n\n    if (seen.has(normalizedExtension)) continue\n    seen.add(normalizedExtension)\n    normalizedExtensions.push(normalizedExtension)\n  }\n\n  let transforms = files.transforms ?? {}\n  if (transforms === null || typeof transforms !== 'object' || Array.isArray(transforms)) {\n    throw new TypeError('files.transforms must be an object')","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/assets/src/lib/files/config.ts#L179-L215","documentation":"After normalization (trim + lowercase), every `files.extensions` entry must match `.ext` — a leading dot followed only by letters, digits, underscores, or hyphens, with no additional dots or slashes. This error fires for malformed entries like `css`, `.tar.gz`, or `./css`.","triggerScenarios":"Passing `extensions: ['css']` (missing dot), `.tar.gz` (two segments), `.d.ts`, or entries containing path separators or whitespace that leaves invalid characters.","commonSituations":"Developers omitting the leading dot out of habit from other tools' extension lists; multi-part extensions; extensions with special characters like `!` or `+` which the regex rejects.","solutions":["Format each entry as a single dotted extension: `['.css', '.md', '.tar']` (use only the final segment for multi-part extensions)","Add the leading dot programmatically if needed: `ext.startsWith('.') ? ext : '.' + ext`","Double the leading dot issue in env-var-derived lists"],"exampleFix":"// before\nfiles: { extensions: ['css', 'md'] }\n// after\nfiles: { extensions: ['.css', '.md'] }","handlingStrategy":"validation","validationCode":"let extensions = raw.map((e: string) => (e.startsWith('.') ? e : '.' + e)).filter((e: string) => /^\\.[A-Za-z0-9_-]+$/.test(e))","typeGuard":"function isDotExt(e: string): boolean {\n  return /^\\.[A-Za-z0-9_-]+$/.test(e.trim().toLowerCase())\n}","tryCatchPattern":null,"preventionTips":["Get in the habit of the leading dot in extension lists","Validate extensions against the regex when lists are user-supplied"],"tags":["config","files","extensions","format-validation"],"backgroundTag":"config-format-validation","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}