webpack/webpack · error · Error
request should be a string or object with loader and options
Error message
request should be a string or object with loader and options (${JSON.stringify(value)}) What it means
Inside the vendored loader-runner, a LoaderObject's request setter accepts either a string (parsed into path/query/fragment) or a descriptor object. The descriptor path requires a `loader` string property (the loader's resolved path); the setter throws when value is an object lacking `loader`. The thrown message stringifies the bad value for diagnosis. This is hit when webpack/NormalModule resolves and assigns loader descriptors from module.rules.
Source
Thrown at lib/loaders/LoaderRunner.js:161
return escapeHash(this.path) + escapeHash(this.query) + this.fragment;
}
/**
* @param {LoaderItemInput} value loader request or descriptor
*/
set request(value) {
if (typeof value === "string") {
const { path, query, fragment } = parseResource(value);
this.path = path;
this.query = query;
this.fragment = fragment;
this.options = undefined;
this.ident = undefined;
return;
}
if (!value.loader) {
throw new Error(
`request should be a string or object with loader and options (${JSON.stringify(
value
)})`
);
}
const { loader: path, fragment, type, options, ident } = value;
this.path = path;
this.fragment = fragment || "";
this.type = type;
this.options = options;
this.ident = ident;
if (options === null || options === undefined) {
this.query = "";
} else if (typeof options === "string") {
this.query = `?${options}`;
} else if (ident) {View on GitHub (pinned to 318421ea8a)
Solutions
- Inspect the offending rule/use entry and ensure each object has a `loader` string (e.g. { loader: 'babel-loader', options: {...} }).
- If using a function in module.rules.use, verify it returns { loader, options } and never an options-only object.
- Check any custom NormalModuleFactory tap that mutates or injects loader descriptors.
Example fix
// before
module.exports = {
module: { rules: [{ test: /\.js$/, use: [{ options: { presets: ['@babel/env'] } }] }] }
};
// after
module.exports = {
module: { rules: [{ test: /\.js$/, use: [{ loader: 'babel-loader', options: { presets: ['@babel/env'] } }] }] }
}; Defensive patterns
Strategy: type-guard
Validate before calling
// Validate module.rules.use entries before passing to webpack
function normalizeRules(rules) {
for (const rule of rules) {
const uses = Array.isArray(rule.use) ? rule.use : (rule.use ? [rule.use] : []);
for (const u of uses) {
if (u && typeof u === 'object' && !('loader' in u) && !u.loader) {
throw new Error('Rule use entry is missing a "loader" field: ' + JSON.stringify(u));
}
}
}
} Type guard
/** @typedef {{ loader: string, options?: object }} LoaderDescriptor */
/** @param {unknown} u
* @returns {u is LoaderDescriptor} */
function isLoaderDescriptor(u) {
return !!u && typeof u === 'object' && typeof u.loader === 'string';
} Prevention
- Always include `loader` in every object form of module.rules.use.
- If a use callback returns an object, type-check its return value against the loader-descriptor shape.
When it happens
Trigger: A rule's `use`/`loader` entry resolves to an object without a `loader` key, e.g. { options: {...} } with no loader, or a malformed loader descriptor returned by a custom resolve hook. Also reachable if a plugin taps NormalModuleFactory and emits a bad loader descriptor.
Common situations: Typos in module.rules (writing { options: {} } instead of { loader: 'babel-loader', options: {} }). A plugin or custom resolver that constructs loader descriptors dynamically and forgets the loader field. Corrupted/empty loader string after path resolution.
Related errors
- Cannot parse string options: ${/** @type {Error} */ (err).me
- loader options must be string or object
- No ident is provided by referenced loader. When using a func
- Invalid ident is provided by referenced loader
- No parser registered for ${type}
AI-assisted analysis of webpack/webpack@318421ea8a (2026-08-03).
Data as JSON: /data/errors/a05f55d8bf18e048.json.
Report an issue: GitHub.