remix-run/remix · error · TypeError
${optionPath} values must be strings
Error message
${optionPath} values must be strings What it means
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.
Source
Thrown at packages/assets/src/lib/files/config.ts:333
function normalizeTransformExtensions(
extensions: readonly string[] | undefined,
optionPath: string,
): readonly string[] | undefined {
if (extensions === undefined) return undefined
if (!Array.isArray(extensions)) {
throw new TypeError(`${optionPath} must be an array`)
}
if (extensions.length === 0) {
throw new TypeError(`${optionPath} must include at least one extension`)
}
let normalizedExtensions: string[] = []
let seen = new Set<string>()
for (let extension of extensions) {
if (typeof extension !== 'string') {
throw new TypeError(`${optionPath} values must be strings`)
}
let normalizedExtension = extension.trim().toLowerCase()
if (!/^\.[A-Za-z0-9_-]+$/.test(normalizedExtension)) {
throw new TypeError(`${optionPath} values must use ".ext" format. Received "${extension}".`)
}
if (seen.has(normalizedExtension)) continue
seen.add(normalizedExtension)
normalizedExtensions.push(normalizedExtension)
}
return normalizedExtensions
}
export function serializeAssetTransformInvocations<transforms extends AssetRequestTransformMap>(
transforms: readonly AssetTransformInvocation<transforms>[],
transformsByName: ResolvedAssetRequestTransformMap,View on GitHub (pinned to 9696913134)
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
Example fix
// before
files: { transformExtensions: ['.css', 42] }
// after
files: { transformExtensions: ['.css', '.js'] } Defensive patterns
Strategy: validation
Validate before calling
if (!ext.every((e) => typeof e === 'string')) ext = ext.map(String)
Type guard
function isStringArray(v: unknown): v is readonly string[] {
return Array.isArray(v) && v.every((e) => typeof e === 'string')
} Prevention
- Validate JSON config shapes before passing to the library
- Avoid `as any` casts on config objects
When it happens
Trigger: Passing an array containing numbers, null, or objects, e.g. `['.css', 42]` or `[null]`, to resolveAssetServerOptions.
Common situations: Mixed arrays from JSON config with loose typing, arrays built with `.concat(someNumber)`, or TS types bypassed with `as any`.
Understand the failure class
Background: Invalid option value errors: "must be one of", "is not a valid", and "only allows" failures explained — this error's family across 23 libraries.
Related errors
- files.maxRequestTransforms must be a positive integer
- ${optionPath} must be an array
- ${optionPath} must include at least one extension
- ${optionPath} values must use ".ext" format. Received "${ext
- ${optionPath} must be an object
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/3613fcd9116703f2.
Report an issue: GitHub.