{"record":{"id":"be9801da12ada75b","repo":"remix-run/remix","slug":"files-extensions-values-must-be-strings","errorCode":null,"errorMessage":"files.extensions values must be strings","messagePattern":"files\\.extensions values must be strings","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/assets/src/lib/files/config.ts","lineNumber":192,"sourceCode":"    return {\n      extensions: [],\n      globalTransforms: [],\n      hasTransforms: false,\n      maxRequestTransforms: defaultMaxRequestTransforms,\n      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)","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/assets/src/lib/files/config.ts#L174-L210","documentation":"Each entry in `files.extensions` must be a string so it can be normalized (trimmed, lowercased) and validated as an extension. This error fires when an array entry is a number, boolean, null, or object.","triggerScenarios":"`files: { extensions: ['.css', 42] }` or any array containing a non-string element.","commonSituations":"Env-var-derived lists parsed without mapping to strings (`.split()` returns strings, but JSON.parse of mixed arrays may not); YAML booleans/numbers sneaking into extension lists; typos like `extensions: ['.css', null]` from optional fields.","solutions":["Map entries to strings: `extensions: rawExtensions.map(String)` or filter invalid entries","Fix the source config so every extension is a quoted string","Validate the list before passing it (see validation code)"],"exampleFix":"// before\nfiles: { extensions: ['.css', env.MD_EXT_VERSION] }\n// after\nfiles: { extensions: ['.css', String(env.MD_EXT_VERSION)] }","handlingStrategy":"validation","validationCode":"if (!files.extensions.every((e) => typeof e === 'string')) {\n  files.extensions = files.extensions.map(String)\n}","typeGuard":"function isStringArray(v: unknown[]): v is string[] {\n  return v.every((e) => typeof e === 'string')\n}","tryCatchPattern":null,"preventionTips":["Normalize env/JSON-derived lists with .map(String) up front","Avoid mixed-type config arrays"],"tags":["config","files","validation","extensions"],"backgroundTag":"config-type-validation","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}