remix-run/remix · error · TypeError
${optionPath} must use a single numeric year like "2020"
Error message
${optionPath} must use a single numeric year like "2020" What it means
Thrown when a script target version string is not composed solely of digits. The ES script target only accepts a plain numeric year, so values like 'es2020', '2020.1', 'v2020', or '20x20' are rejected before the four-digit/2015+ check runs.
Source
Thrown at packages/assets/src/lib/target.ts:158
`${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`)
}
if (value.trim().length === 0) {
throw new TypeError(`${optionPath} must be a non-empty string`)
}
View on GitHub (pinned to 9696913134)
Solutions
- Use a bare four-digit year without prefix: '2020' instead of 'es2020'
- If migrating from tsconfig, strip the 'es' prefix programmatically
- Double-check for stray characters, spaces, or quotes introduced by env vars or config templating
Example fix
// before
let target = { script: { es: 'es2020' } }
// after
let target = { script: { es: '2020' } } Defensive patterns
Strategy: validation
Validate before calling
let isValidEsYear = (value: string) => /^\d+$/.test(value.trim())
if (!isValidEsYear(esTarget)) throw new Error('script target must be a plain numeric year') Type guard
let isPlainNumericYear = (value: unknown): value is string => typeof value === 'string' && /^\d+$/.test(value.trim())
Prevention
- Do not reuse tsconfig target values like 'es2020'; strip the es prefix first
- Keep a single canonical example of valid target config in your repo docs
When it happens
Trigger: `{ script: { es: 'es2020' } }` (including the es prefix), `'latest'`, `'ES2020'`, `'2020-01'`, or any value with non-digit characters after trimming.
Common situations: Copying values from tsconfig `target` (which accepts lowercase years like 'es2020') into the assets target option; passing browserslist-style names; assuming the API accepts the same values as TypeScript or 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
- ${optionPath} must use a four-digit year of 2015 or higher
- 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
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/d217729ef4c55324.
Report an issue: GitHub.