evanw/esbuild · error · Error
onLoad() call is missing a filter
Error message
onLoad() call is missing a filter
What it means
onLoad registrations, like onResolve (49), require a RegExp filter so the native binary can pre-filter which paths invoke the JS callback. lib/shared/common.ts:1323 throws when filter is null after mustBeRegExp validation. The same performance rationale applies: without a filter every file load would round-trip into JS.
Source
Thrown at lib/shared/common.ts:1323
let registeredNote = extractCallerV8(new Error(registeredText), streamIn, 'onResolve')
let keys: OptionKeys = {}
let filter = getFlag(options, keys, 'filter', mustBeRegExp)
let namespace = getFlag(options, keys, 'namespace', mustBeString)
checkForInvalidFlags(options, keys, `in onResolve() call for plugin ${quote(name)}`)
if (filter == null) throw new Error(`onResolve() call is missing a filter`)
let id = nextCallbackID++
onResolveCallbacks[id] = { name: name!, callback, note: registeredNote }
plugin.onResolve.push({ id, filter: jsRegExpToGoRegExp(filter), namespace: namespace || '' })
},
onLoad(options, callback) {
let registeredText = `This error came from the "onLoad" callback registered here:`
let registeredNote = extractCallerV8(new Error(registeredText), streamIn, 'onLoad')
let keys: OptionKeys = {}
let filter = getFlag(options, keys, 'filter', mustBeRegExp)
let namespace = getFlag(options, keys, 'namespace', mustBeString)
checkForInvalidFlags(options, keys, `in onLoad() call for plugin ${quote(name)}`)
if (filter == null) throw new Error(`onLoad() call is missing a filter`)
let id = nextCallbackID++
onLoadCallbacks[id] = { name: name!, callback, note: registeredNote }
plugin.onLoad.push({ id, filter: jsRegExpToGoRegExp(filter), namespace: namespace || '' })
},
onDispose(callback) {
onDisposeCallbacks.push(callback)
},
esbuild: streamIn.esbuild,
})
// Await a returned promise if there was one. This allows plugins to do
// some asynchronous setup while still retaining the ability to modify
// the build options. This deliberately serializes asynchronous plugin
// setup instead of running them concurrently so that build option
// modifications are easier to reason about.
if (promise) await promiseView on GitHub (pinned to 6ff1d8b0d8)
Solutions
- Pass a RegExp filter, e.g. build.onLoad({ filter: /.*/, namespace: 'mem' }, cb) for a virtual namespace.
- Scope the filter tightly: /\.txt$/ to only handle certain extensions.
- Convert any glob from config to a RegExp once at plugin init.
Example fix
// before
build.onLoad({ namespace: 'mem' }, args => { /* ... */ });
// after
build.onLoad({ filter: /.*/, namespace: 'mem' }, args => { /* ... */ }); Defensive patterns
Strategy: type-guard
Validate before calling
function registerOnLoad(build, opts, cb) {
if (!(opts?.filter instanceof RegExp)) {
throw new TypeError('onLoad requires options.filter to be a RegExp');
}
build.onLoad(opts, cb);
} Type guard
function hasLoadFilter(o): o is { filter: RegExp; namespace?: string } {
return !!o && o.filter instanceof RegExp;
} Prevention
- Pass filter: /.*/ when using a virtual namespace alongside namespace: 'mem'.
- Convert config globs to RegExp once at plugin init.
- Type the options object so the compiler requires filter.
When it happens
Trigger: build.onLoad({}, cb). build.onLoad({ filter: '.js' }, cb) (string). build.onLoad({ namespace: 'mem' }, cb) forgetting filter — note filter and namespace together are the typical combo.
Common situations: Virtual namespace plugins (e.g. in-memory FS) that intend to match everything but forget filter: /.*/ . Copy-paste from onResolve examples where filter is shown but namespace omitted. Passing a glob from a config that wasn't converted to RegExp.
Related errors
- onResolve() call is missing a filter
- Expected onLoad() callback in plugin ${quote(name)} to retur
- Plugin at index ${i} must be an object
- Plugin at index ${i} is missing a name
- Plugin is missing a setup function
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/1b80e995ad53fd1f.json.
Report an issue: GitHub.