evanw/esbuild · error · Error
Expected onResolve() callback in plugin ${quote(name)} to re
Error message
Expected onResolve() callback in plugin ${quote(name)} to return an object What it means
An onResolve callback can return an object describing how to resolve the matched import ({ path?, namespace?, external?, errors?, warnings?, ... }) or nothing to defer to the next plugin. lib/shared/common.ts:1395 throws when the callback returns a non-null non-object — the protocol can only serialise the well-known object shape. A bare string path or a boolean external is not accepted; they must be wrapped.
Source
Thrown at lib/shared/common.ts:1395
}
requestCallbacks['on-resolve'] = async (id, request: protocol.OnResolveRequest) => {
let response: protocol.OnResolveResponse = {}, name = '', callback, note
for (let id of request.ids) {
try {
({ name, callback, note } = onResolveCallbacks[id])
let result = await callback({
path: request.path,
importer: request.importer,
namespace: request.namespace,
resolveDir: request.resolveDir,
kind: request.kind,
pluginData: details.load(request.pluginData),
with: request.with,
})
if (result != null) {
if (typeof result !== 'object') throw new Error(`Expected onResolve() callback in plugin ${quote(name)} to return an object`)
let keys: OptionKeys = {}
let pluginName = getFlag(result, keys, 'pluginName', mustBeString)
let path = getFlag(result, keys, 'path', mustBeString)
let namespace = getFlag(result, keys, 'namespace', mustBeString)
let suffix = getFlag(result, keys, 'suffix', mustBeString)
let external = getFlag(result, keys, 'external', mustBeBoolean)
let sideEffects = getFlag(result, keys, 'sideEffects', mustBeBoolean)
let pluginData = getFlag(result, keys, 'pluginData', canBeAnything)
let errors = getFlag(result, keys, 'errors', mustBeArray)
let warnings = getFlag(result, keys, 'warnings', mustBeArray)
let watchFiles = getFlag(result, keys, 'watchFiles', mustBeArrayOfStrings)
let watchDirs = getFlag(result, keys, 'watchDirs', mustBeArrayOfStrings)
checkForInvalidFlags(result, keys, `from onResolve() callback in plugin ${quote(name)}`)
response.id = id
if (pluginName != null) response.pluginName = pluginName
if (path != null) response.path = path
if (namespace != null) response.namespace = namespaceView on GitHub (pinned to 6ff1d8b0d8)
Solutions
- Wrap the result: onResolve(args => ({ path: args.path.replace(/^old/, 'new') })).
- To mark external: onResolve(args => ({ external: true })).
- Return undefined/null when the plugin declines to handle the import.
Example fix
// before
build.onResolve({ filter: /^old:/ }, args => args.path.replace(/^old:/, ''));
// after
build.onResolve({ filter: /^old:/ }, args => ({ path: args.path.replace(/^old:/, '') })); Defensive patterns
Strategy: validation
Validate before calling
function wrapOnResolve(build, opts, cb) {
build.onResolve(opts, async (args) => {
const r = await cb(args);
if (r != null && typeof r !== 'object') {
throw new TypeError('onResolve callback must return an object { path?, external?, ... } or void');
}
return r as any;
});
} Type guard
function isResolveResult(v): v is import('esbuild').OnResolveResult | null | undefined {
return v == null || (typeof v === 'object' && typeof (v as any).then !== 'function');
} Prevention
- Return { path } not a bare string from onResolve callbacks.
- Return undefined to defer to the next plugin/esbuild's resolver.
- Wrap external flags as { external: true }.
When it happens
Trigger: Returning a string path directly: onResolve(args => args.path.replace('old', 'new')). Returning { external: true } is fine, but onResolve(() => true) is not. Returning an array of candidates.
Common situations: Plugin meant to redirect imports returns the bare new path string. Author confuses onResolve semantics with onEnd (which returns { errors, warnings }). Refactor that returns the raw output of String.replace.
Related errors
- onResolve() call is missing a filter
- Expected onStart() callback in plugin ${quote(name)} to retu
- Expected onLoad() callback in plugin ${quote(name)} to retur
- Expected onEnd() callback in plugin ${quote(name)} to return
- Plugin at index ${i} must be an object
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/da56e5a5331c0cc6.json.
Report an issue: GitHub.