remix-run/remix · error · TypeError
${optionPath} must be an object
Error message
${optionPath} must be an object What it means
The `script.target` option (e.g. `script: { target: ... }` or build.target) must be a plain object mapping target names to versions, not a string, array, or class instance. normalizeScriptTargetObject duck-checks with isPlainObject and throws otherwise.
Source
Thrown at packages/assets/src/lib/target.ts:94
let lightningCssTargets: ResolvedStyleTarget = {}
for (let browserTargetName of browserTargetNames) {
let version = resolvedTarget[browserTargetName]
if (version == null) continue
lightningCssTargets[lightningCssTargetNameByBrowserTargetName[browserTargetName]] =
toLightningCssTargetVersion(version)
}
return lightningCssTargets
}
function normalizeScriptTargetObject(
target: AssetTarget | undefined,
optionPath: string,
): NormalizedAssetTarget | undefined {
if (target == null) return undefined
if (!isPlainObject(target)) {
throw new TypeError(`${optionPath} must be an object`)
}
let normalizedTarget: NormalizedAssetTarget = {}
for (let [key, value] of Object.entries(target)) {
if (key === 'es') {
normalizedTarget.es = normalizeScriptTargetVersion(value, `${optionPath}.es`)
continue
}
if (!browserTargetNameSet.has(key)) {
throw new TypeError(`${optionPath}.${key} is not a supported target`)
}
normalizedTarget[key as BrowserTargetName] = normalizeBrowserTargetVersion(
value,
`${optionPath}.${key}`,
)View on GitHub (pinned to 9696913134)
Solutions
- Use the object form: { es: 2022 } or { chrome: '100' }
- When accepting string config from users, convert 'es2022' to { es: 2022 } before passing
- Ensure the value isn't a class instance — use a plain object literal
Example fix
// before
script: { target: 'es2022' }
// after
script: { target: { es: 2022 } } Defensive patterns
Strategy: type-guard
Validate before calling
if (target != null && (typeof target !== 'object' || Array.isArray(target))) {
throw new Error('script.target must be an object like { es: 2022 }')
} Type guard
function isPlainTargetObject(v: unknown): v is Record<string, string | number | boolean> {
return v != null && typeof v === 'object' && !Array.isArray(v)
&& Object.getPrototypeOf(v) === Object.prototype
} Prevention
- Use object literals for target, not classes or strings
- Convert esbuild-style string targets before passing
When it happens
Trigger: Passing `script: { target: 'es2022' }` (string), `target: ['es2022']` (array), or a class instance with target fields to the assets options.
Common situations: Porting esbuild/browserslist config where target is a string or array; spreading a config class instance; JSON configs typed loosely.
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
- ${optionPath}.${key} is not a supported target
- files.maxRequestTransforms must be a positive integer
- ${optionPath} must be an array
- ${optionPath} must include at least one extension
- ${optionPath} values must be strings
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/d6e1f03622b64f6a.
Report an issue: GitHub.