remix-run/remix · error · TypeError
${optionPath} must use a four-digit year of 2015 or higher
Error message
${optionPath} must use a four-digit year of 2015 or higher What it means
Thrown when a script target version is numeric but not a valid four-digit year of at least 2015. The assets normalizer maps the year to an esbuild-style target like es2020, and esbuild's lowest supported ES target is es2015, so shorter numbers, future/non-4-digit numbers, or years below 2015 fail.
Source
Thrown at packages/assets/src/lib/target.ts:162
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`)
}
if (!/^\d+(\.\d+){0,2}$/.test(value)) {
throw new TypeError(`${optionPath} must use "X", "X.Y", or "X.Y.Z" version format`)
}
View on GitHub (pinned to 9696913134)
Solutions
- Use a four-digit year >= 2015, e.g. '2020' or '2022'
- For legacy ES5 output, pick '2015' as the oldest supported value and verify your toolchain
- Validate year ranges in config UIs or scripts before submission
Example fix
// before
let target = { script: { es: '6' } }
// after
let target = { script: { es: '2020' } } Defensive patterns
Strategy: validation
Validate before calling
let isValidEsYear = (value: string) => /^\d{4}$/.test(value) && Number(value) >= 2015
if (!isValidEsYear(esTarget)) throw new Error('script target must be a 4-digit year >= 2015') Type guard
let isSupportedEsYear = (value: unknown): value is string =>
typeof value === 'string' && /^\d{4}$/.test(value.trim()) && Number(value.trim()) >= 2015 Prevention
- Use a shared constant like const ES_TARGET = '2020' in config
- Avoid ES5/ES6-style level numbers; only years 2015+ are supported
When it happens
Trigger: `{ script: { es: '5' } }`, `{ script: { es: '20' } }`, `{ script: { es: '2014' } }`, or `{ script: { es: '302020' } }`.
Common situations: Using an ES level like '5' or '6' (as in tsconfig's ES5/ES6 naming) instead of a year; typos in the year; assuming any number is accepted because the earlier check only validates digits.
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 single numeric year like "2020"
- 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/80eacac9e5f0c5c7.
Report an issue: GitHub.