babel/babel · error · Error
${msg(loc)} must be a string, an array of strings or an obje
Error message
${msg(loc)} must be a string, an array of strings or an object What it means
Thrown by assertTargets when the 'targets' option is neither a browserslist query string (e.g. '> 0.25%'), an array of such strings, nor a plain object keyed by target names. Numbers, booleans, nested arrays, or non-browserslist values are rejected.
Source
Thrown at packages/babel-core/src/config/validation/option-assertions.ts:402
function assertPluginTarget(loc: GeneralPath, value: unknown): PluginTarget {
if (
(typeof value !== "object" || !value) &&
typeof value !== "string" &&
typeof value !== "function"
) {
throw new Error(`${msg(loc)} must be a string, object, function`);
}
return value as PluginTarget;
}
export function assertTargets(
loc: GeneralPath,
value: any,
): TargetsListOrObject {
if (isBrowsersQueryValid(value)) return value;
if (typeof value !== "object" || !value || Array.isArray(value)) {
throw new Error(
`${msg(loc)} must be a string, an array of strings or an object`,
);
}
const browsersLoc = access(loc, "browsers");
const esmodulesLoc = access(loc, "esmodules");
assertBrowsersList(browsersLoc, value.browsers);
assertBoolean(esmodulesLoc, value.esmodules);
for (const key of Object.keys(value)) {
const val = value[key];
const subLoc = access(loc, key);
if (key === "esmodules") assertBoolean(subLoc, val);
else if (key === "browsers") assertBrowsersList(subLoc, val);
else if (!Object.hasOwn(TargetNames, key)) {
const validTargets = Object.keys(TargetNames).join(", ");View on GitHub (pinned to 06b6eae39d)
Solutions
- Use a browserslist query string: targets: '> 0.25%, not dead'.
- Use an array of queries: targets: ['> 1%', 'last 2 versions'].
- Use an object keyed by target: targets: { chrome: 80, firefox: 78 }.
Example fix
// before
{ targets: 90 }
// after
{ targets: { chrome: 90 } } Defensive patterns
Strategy: type-guard
Validate before calling
function isTargetsShape(v) {
if (typeof v === 'string') return true;
if (Array.isArray(v)) return v.every((x) => typeof x === 'string');
if (typeof v === 'object' && v !== null) return true;
return false;
}
if (opts.targets !== undefined && !isTargetsShape(opts.targets)) {
throw new Error('targets must be string, array of strings, or object');
} Type guard
function isTargetsShape(
v: unknown,
): v is string | string[] | Record<string, unknown> {
if (typeof v === 'string') return true;
if (Array.isArray(v)) return v.every((x) => typeof x === 'string');
return typeof v === 'object' && v !== null;
} Try / catch
try {
babel.loadOptions(opts);
} catch (e) {
if (e instanceof Error && /must be a string, an array of strings or an object/.test(e.message)) {
opts.targets = undefined; // fall back to .browserslistrc
return babel.loadOptions(opts);
}
throw e;
} Prevention
- Use a browserslist query string for broad targets.
- Use an object for per-browser version pins.
- Defer to .browserslistrc by omitting targets.
When it happens
Trigger: targets: 90; targets: true; targets: [['chrome', 80]] (nested array); targets: 'last 2 versions' is fine, but targets: { chrome: 'latest' } shape errors later (see 78). Here the top-level type itself is wrong.
Common situations: Passing a numeric browser version as the whole targets value; migrating from browserslist config expecting a different shape; JSON configs that wrap targets in an extra array.
Related errors
- ${msg(subLoc)} is not a valid target. Supported targets are
- ${msg(loc)} must be undefined, a string or an array of strin
- ${msg(loc)} must be a undefined, a boolean, a string, got ${
- ${msg(loc)} must be a boolean, object, or undefined
- ${msg(loc)} must be a string, or undefined
AI-assisted analysis of babel/babel@06b6eae39d (2026-08-03).
Data as JSON: /data/errors/5c553f53afb1fab9.json.
Report an issue: GitHub.