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

  1. Prefix each entry with a dot and keep it to one segment: ['.css']
  2. When deriving from filenames, use extname and keep the dot
  3. 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

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


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/37a4b5493b353883. Report an issue: GitHub.