{"record":{"id":"be9f393c265f3e0f","repo":"remix-run/remix","slug":"optionpath-must-include-at-least-one-extension","errorCode":null,"errorMessage":"${optionPath} must include at least one extension","messagePattern":"(.+?) must include at least one extension","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/assets/src/lib/files/config.ts","lineNumber":325,"sourceCode":"    cache: files.cache,\n    extensions: normalizedExtensions,\n    globalTransforms: normalizedGlobalTransforms,\n    hasTransforms: normalizedTransforms.size > 0 || normalizedGlobalTransforms.length > 0,\n    maxRequestTransforms,\n    transforms: normalizedTransforms,\n  }\n}\n\nfunction normalizeTransformExtensions(\n  extensions: readonly string[] | undefined,\n  optionPath: string,\n): readonly string[] | undefined {\n  if (extensions === undefined) return undefined\n  if (!Array.isArray(extensions)) {\n    throw new TypeError(`${optionPath} must be an array`)\n  }\n  if (extensions.length === 0) {\n    throw new TypeError(`${optionPath} must include at least one extension`)\n  }\n\n  let normalizedExtensions: string[] = []\n  let seen = new Set<string>()\n\n  for (let extension of extensions) {\n    if (typeof extension !== 'string') {\n      throw new TypeError(`${optionPath} values must be strings`)\n    }\n\n    let normalizedExtension = extension.trim().toLowerCase()\n    if (!/^\\.[A-Za-z0-9_-]+$/.test(normalizedExtension)) {\n      throw new TypeError(`${optionPath} values must use \".ext\" format. Received \"${extension}\".`)\n    }\n\n    if (seen.has(normalizedExtension)) continue\n    seen.add(normalizedExtension)\n    normalizedExtensions.push(normalizedExtension)","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/assets/src/lib/files/config.ts#L307-L343","documentation":"Extension lists such as `files.transformExtensions` cannot be empty arrays — at least one extension is required for the option to be meaningful. An explicit `[]` is treated as a configuration mistake rather than 'none'.","triggerScenarios":"Passing `files: { transformExtensions: [] }`, or a dynamically-built array that ends up empty after filtering (e.g. splitting an empty env var).","commonSituations":"Config generated from user input or env where filtering removes all entries; intentionally trying to disable transforms with an empty array instead of omitting the option.","solutions":["Omit the option entirely if you want defaults or no customization","Ensure dynamic arrays have at least one entry before passing","Guard with `extensions.length > 0` when building config from untrusted input"],"exampleFix":"// before\nfiles: { transformExtensions: [] }\n// after\nfiles: { transformExtensions: ['.css', '.js'] }","handlingStrategy":"validation","validationCode":"if (Array.isArray(ext) && ext.length === 0) throw new Error('transformExtensions needs >=1 entry')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use `ext?.length ? ext : undefined` when building config dynamically","Treat empty as 'unset', not as 'disable'"],"tags":["config","validation","assets"],"backgroundTag":"invalid-option-value","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}