{"record":{"id":"3613fcd9116703f2","repo":"remix-run/remix","slug":"optionpath-values-must-be-strings","errorCode":null,"errorMessage":"${optionPath} values must be strings","messagePattern":"(.+?) values must be strings","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/assets/src/lib/files/config.ts","lineNumber":333,"sourceCode":"\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)\n  }\n\n  return normalizedExtensions\n}\n\nexport function serializeAssetTransformInvocations<transforms extends AssetRequestTransformMap>(\n  transforms: readonly AssetTransformInvocation<transforms>[],\n  transformsByName: ResolvedAssetRequestTransformMap,","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/assets/src/lib/files/config.ts#L315-L351","documentation":"Every entry in an extension list option (e.g. `files.transformExtensions`) must be a string. The validator iterates the array and throws on the first non-string entry.","triggerScenarios":"Passing an array containing numbers, null, or objects, e.g. `['.css', 42]` or `[null]`, to resolveAssetServerOptions.","commonSituations":"Mixed arrays from JSON config with loose typing, arrays built with `.concat(someNumber)`, or TS types bypassed with `as any`.","solutions":["Convert entries to strings before passing: extensions.map(String)","Fix the source producing non-string entries (validate JSON schema of your config)","Type the variable as readonly string[] so TS catches it earlier"],"exampleFix":"// before\nfiles: { transformExtensions: ['.css', 42] }\n// after\nfiles: { transformExtensions: ['.css', '.js'] }","handlingStrategy":"validation","validationCode":"if (!ext.every((e) => typeof e === 'string')) ext = ext.map(String)","typeGuard":"function isStringArray(v: unknown): v is readonly string[] {\n  return Array.isArray(v) && v.every((e) => typeof e === 'string')\n}","tryCatchPattern":null,"preventionTips":["Validate JSON config shapes before passing to the library","Avoid `as any` casts on config objects"],"tags":["config","validation","assets","typescript"],"backgroundTag":"invalid-option-value","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}