evanw/esbuild · error · Error

onResolve() call is missing a filter

Error message

onResolve() call is missing a filter

What it means

esbuild uses a RegExp 'filter' on every onResolve registration so the Go side can cheaply skip callbacks for irrelevant paths without crossing the JS↔Go boundary. At lib/shared/common.ts:1310 the registration throws if filter is null after getFlag(filter, mustBeRegExp) — mustBeRegExp requires value instanceof RegExp. Without a filter esbuild would have to call back for every single import, which destroys performance, so the field is mandatory.

Source

Thrown at lib/shared/common.ts:1310

          onStartCallbacks.push({ name: name!, callback, note: registeredNote })
          plugin.onStart = true
        },

        onEnd(callback) {
          let registeredText = `This error came from the "onEnd" callback registered here:`
          let registeredNote = extractCallerV8(new Error(registeredText), streamIn, 'onEnd')
          onEndCallbacks.push({ name: name!, callback, note: registeredNote })
          plugin.onEnd = true
        },

        onResolve(options, callback) {
          let registeredText = `This error came from the "onResolve" callback registered here:`
          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 || '' })
        },

View on GitHub (pinned to 6ff1d8b0d8)

Solutions

  1. Pass a RegExp: build.onResolve({ filter: /\.css$/ }, cb).
  2. Use /.*/ only as a last resort (it disables the perf optimisation); prefer a tight pattern.
  3. Remember the regex is matched against the unresolved path; escape dots: /\.css$/ not /.css$/.

Example fix

// before
build.onResolve({ filter: '*.css' }, args => { /* ... */ });
// after
build.onResolve({ filter: /\.css$/ }, args => { /* ... */ });
Defensive patterns

Strategy: type-guard

Validate before calling

function registerOnResolve(build, opts, cb) {
  if (!(opts?.filter instanceof RegExp)) {
    throw new TypeError('onResolve requires options.filter to be a RegExp');
  }
  build.onResolve(opts, cb);
}

Type guard

function hasRegExpFilter(o): o is { filter: RegExp } {
  return !!o && o.filter instanceof RegExp;
}

Prevention

When it happens

Trigger: build.onResolve({}, cb) — no filter. build.onResolve({ filter: './foo' }, cb) — string instead of RegExp. build.onResolve({ filter: /.*/ , namespace: undefined }) is fine, but forgetting filter entirely throws.

Common situations: Devs pass a glob string (e.g. '*.css') thinking esbuild supports glob syntax; it must be a RegExp. Misreading docs that show optional namespace and assuming filter is optional too. Object spread that drops filter.

Related errors


AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03). Data as JSON: /data/errors/35cb325322428130.json. Report an issue: GitHub.