{"record":{"id":"41303f228cfc60e8","repo":"remix-run/remix","slug":"optionpath-must-be-a-string","errorCode":null,"errorMessage":"${optionPath} must be a string","messagePattern":"(.+?) must be a string","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/assets/src/lib/target.ts","lineNumber":149,"sourceCode":"      continue\n    }\n\n    if (!browserTargetNameSet.has(key)) {\n      throw new TypeError(`${optionPath}.${key} is not a supported target`)\n    }\n\n    normalizedTarget[key as BrowserTargetName] = normalizeBrowserTargetVersion(\n      value,\n      `${optionPath}.${key}`,\n    )\n  }\n\n  return Object.keys(normalizedTarget).length === 0 ? undefined : normalizedTarget\n}\n\nfunction normalizeScriptTargetVersion(value: unknown, optionPath: string): AssetTarget['es'] {\n  if (typeof value !== 'string') {\n    throw new TypeError(`${optionPath} must be a string`)\n  }\n\n  let normalizedValue = value.trim()\n  if (normalizedValue.length === 0) {\n    throw new TypeError(`${optionPath} must be a non-empty string`)\n  }\n\n  if (!/^\\d+$/.test(normalizedValue)) {\n    throw new TypeError(`${optionPath} must use a single numeric year like \"2020\"`)\n  }\n\n  if (!/^\\d{4}$/.test(normalizedValue) || Number(normalizedValue) < 2015) {\n    throw new TypeError(`${optionPath} must use a four-digit year of 2015 or higher`)\n  }\n\n  return `es${normalizedValue}`\n}\n","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/assets/src/lib/target.ts#L131-L167","documentation":"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.","triggerScenarios":"Passing a non-string value in a target config, e.g. `normalizeTarget({ script: { es: 2020 } })` or `{ script: { chrome: 90 } }` (number instead of \"90\").","commonSituations":"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).","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"],"exampleFix":"// before\nlet target = { script: { es: 2020 } }\n// after\nlet target = { script: { es: '2020' } }","handlingStrategy":"validation","validationCode":"for (let [key, value] of Object.entries(targetConfig.script ?? {})) {\n  if (typeof value !== 'string') {\n    throw new Error(`target.script.${key} must be a string, got ${typeof value}`)\n  }\n}","typeGuard":"let isStringVersion = (value: unknown): value is string => typeof value === 'string'","tryCatchPattern":null,"preventionTips":["Keep all target version values as quoted strings in config files","Run a small config validator before passing options to the assets API"],"tags":["config","validation","build-target"],"backgroundTag":"invalid-config-value","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}