remix-run/remix · error · TypeError
${optionPath} must be a string
Error message
${optionPath} must be a string What it means
Thrown by normalizeScriptTargetVersion in @remix-run/assets when a script target version value is not a string. The assets package normalizes the `script` target option and requires each version value (e.g. `target.script.es` or browser versions) to be a string like "2020". Non-string values such as numbers or booleans are rejected because they cannot be reliably parsed into an ES year.
Source
Thrown at packages/assets/src/lib/target.ts:149
continue
}
if (!browserTargetNameSet.has(key)) {
throw new TypeError(`${optionPath}.${key} is not a supported target`)
}
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}`
}
View on GitHub (pinned to 9696913134)
Solutions
- Quote the version value: use `{ script: { es: '2020' } }` instead of `{ script: { es: 2020 } }`
- Check any config files (JSON/YAML/env-derived) that feed the target option to ensure versions arrive as strings
- If generating config programmatically, coerce with `String(value)` before passing it in
Example fix
// before
let target = { script: { es: 2020 } }
// after
let target = { script: { es: '2020' } } Defensive patterns
Strategy: validation
Validate before calling
for (let [key, value] of Object.entries(targetConfig.script ?? {})) {
if (typeof value !== 'string') {
throw new Error(`target.script.${key} must be a string, got ${typeof value}`)
}
} Type guard
let isStringVersion = (value: unknown): value is string => typeof value === 'string'
Prevention
- Keep all target version values as quoted strings in config files
- Run a small config validator before passing options to the assets API
When it happens
Trigger: Passing a non-string value in a target config, e.g. `normalizeTarget({ script: { es: 2020 } })` or `{ script: { chrome: 90 } }` (number instead of "90").
Common situations: Developers write version numbers as numbers instead of strings in build/asset config; config loaded from JSON or YAML where quotes were dropped; copying config from tools that accept numeric versions (browserslist, esbuild).
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
- basePath must be a string
- fingerprint.buildId must be a string
- fingerprint.buildId must be a non-empty string
- files.extensions must be an array
- files.extensions values must be strings
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/41303f228cfc60e8.
Report an issue: GitHub.