remix-run/remix · error · TypeError

${optionPath} must be an array

Error message

${optionPath} must be an array

What it means

Options like `files.transformExtensions` (and sibling extension lists) must be arrays of strings when provided. normalizeTransformExtensions rejects any non-array value with a TypeError naming the exact option path.

Source

Thrown at packages/assets/src/lib/files/config.ts:322

  }

  return {
    cache: files.cache,
    extensions: normalizedExtensions,
    globalTransforms: normalizedGlobalTransforms,
    hasTransforms: normalizedTransforms.size > 0 || normalizedGlobalTransforms.length > 0,
    maxRequestTransforms,
    transforms: normalizedTransforms,
  }
}

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}".`)
    }

View on GitHub (pinned to 9696913134)

Solutions

  1. Wrap the value in an array: ['.css']
  2. When parsing from env/CLI, split on commas and filter empties
  3. Leave the option undefined to accept the default extension set

Example fix

// before
files: { transformExtensions: '.css' }
// after
files: { transformExtensions: ['.css'] }
Defensive patterns

Strategy: validation

Validate before calling

if (ext !== undefined && !Array.isArray(ext)) throw new Error('transformExtensions must be an array')

Type guard

function isStringArray(v: unknown): v is readonly string[] {
  return Array.isArray(v) && v.every((e) => typeof e === 'string')
}

Prevention

When it happens

Trigger: Passing `files: { transformExtensions: '.css' }` (a string instead of an array), a Set, or a comma-joined string like '.css,.js' to resolveAssetServerOptions.

Common situations: Reading extensions from an env var or CLI arg, which yields a string; spreading a possibly-undefined value; or assuming a single extension can be passed as a bare string.

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/c8baa218c0afac0b. Report an issue: GitHub.