remix-run/remix · error · TypeError
${optionPath} values must use ".ext" format. Received "${ext
Error message
${optionPath} values must use ".ext" format. Received "${extension}". What it means
Extensions in lists like `files.transformExtensions` must start with a dot and contain only alphanumerics, underscores, and hyphens (e.g. `.css`, `.min.js` is not one segment). The value is trimmed and lowercased before the regex check, so failures mean genuinely malformed extensions.
Source
Thrown at packages/assets/src/lib/files/config.ts:338
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,
maxTransforms = defaultMaxRequestTransforms,
): string[] {
if (transforms.length > maxTransforms) {
throw new TypeError(`Expected at most ${maxTransforms} request transforms`)
}View on GitHub (pinned to 9696913134)
Solutions
- Prefix each entry with a dot and keep it to one segment: ['.css']
- When deriving from filenames, use extname and keep the dot
- Strip glob characters and extra dots before passing
Example fix
// before
files: { transformExtensions: ['css', '*.css'] }
// after
files: { transformExtensions: ['.css'] } Defensive patterns
Strategy: validation
Validate before calling
const ok = ext.every((e) => /^\.[A-Za-z0-9_-]+$/.test(e.trim().toLowerCase()))
if (!ok) throw new Error('bad extension format') Type guard
function isValidExtension(e: string): boolean {
return /^\.[A-Za-z0-9_-]+$/.test(e.trim().toLowerCase())
} Prevention
- Always include the leading dot
- Derive extensions with path.extname and normalize to lowercase
When it happens
Trigger: Passing 'css' (missing dot), '.tar.gz' (two dots), '.css?' (invalid chars), or '' (empty string) in the extensions array.
Common situations: Stripping the leading dot for display purposes and forgetting to re-add it; parsing extensions from filenames with `path.extname` inconsistencies; glob patterns like '*.css' passed verbatim.
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 be strings
- ${optionPath} must be an object
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/37a4b5493b353883.
Report an issue: GitHub.