remix-run/remix · error · TypeError

${optionPath} must be a non-empty string

Error message

${optionPath} must be a non-empty string

What it means

Thrown when a script target version string is empty or only whitespace. After confirming the value is a string, the assets normalizer trims it and requires at least one character, because an empty version cannot be mapped to an ES year like es2020.

Source

Thrown at packages/assets/src/lib/target.ts:154

    }

    normalizedTarget[key as BrowserTargetName] = normalizeBrowserTargetVersion(
      value,
      `${optionPath}.${key}`,
    )
  }

  return Object.keys(normalizedTarget).length === 0 ? undefined : normalizedTarget
}

function normalizeScriptTargetVersion(value: unknown, optionPath: string): AssetTarget['es'] {
  if (typeof value !== 'string') {
    throw new TypeError(`${optionPath} must be a string`)
  }

  let normalizedValue = value.trim()
  if (normalizedValue.length === 0) {
    throw new TypeError(`${optionPath} must be a non-empty string`)
  }

  if (!/^\d+$/.test(normalizedValue)) {
    throw new TypeError(`${optionPath} must use a single numeric year like "2020"`)
  }

  if (!/^\d{4}$/.test(normalizedValue) || Number(normalizedValue) < 2015) {
    throw new TypeError(`${optionPath} must use a four-digit year of 2015 or higher`)
  }

  return `es${normalizedValue}`
}

function normalizeBrowserTargetVersion(value: unknown, optionPath: string): AssetTargetVersion {
  if (typeof value !== 'string') {
    throw new TypeError(`${optionPath} must be a string`)
  }

View on GitHub (pinned to 9696913134)

Solutions

  1. Provide a real year such as '2020' for the script target
  2. If the value comes from an env var, check it is set and non-empty before passing it (e.g. `process.env.SCRIPT_TARGET || '2020'`)
  3. Trim/validate user-supplied config before handing it to the assets API

Example fix

// before
let target = { script: { es: process.env.ES_TARGET ?? '' } }
// after
let target = { script: { es: process.env.ES_TARGET || '2020' } }
Defensive patterns

Strategy: validation

Validate before calling

for (let [key, value] of Object.entries(targetConfig.script ?? {})) {
  if (typeof value !== 'string' || value.trim().length === 0) {
    throw new Error(`target.script.${key} must be a non-empty string`)
  }
}

Type guard

let isNonEmptyString = (value: unknown): value is string =>
  typeof value === 'string' && value.trim().length > 0

Prevention

When it happens

Trigger: `normalizeTarget({ script: { es: '' } })` or `{ script: { es: ' ' } })`, or a template/env variable that interpolates to an empty string, e.g. `process.env.SCRIPT_TARGET ?? ''`.

Common situations: Env vars that are unset produce empty strings via `|| ''` or default interpolation; user-facing config forms submitted blank; YAML configs with an empty value (`es:`).

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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