{"id":"35cb325322428130","repo":"evanw/esbuild","slug":"onresolve-call-is-missing-a-filter","errorCode":null,"errorMessage":"onResolve() call is missing a filter","messagePattern":"onResolve\\(\\) call is missing a filter","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/shared/common.ts","lineNumber":1310,"sourceCode":"          onStartCallbacks.push({ name: name!, callback, note: registeredNote })\n          plugin.onStart = true\n        },\n\n        onEnd(callback) {\n          let registeredText = `This error came from the \"onEnd\" callback registered here:`\n          let registeredNote = extractCallerV8(new Error(registeredText), streamIn, 'onEnd')\n          onEndCallbacks.push({ name: name!, callback, note: registeredNote })\n          plugin.onEnd = true\n        },\n\n        onResolve(options, callback) {\n          let registeredText = `This error came from the \"onResolve\" callback registered here:`\n          let registeredNote = extractCallerV8(new Error(registeredText), streamIn, 'onResolve')\n          let keys: OptionKeys = {}\n          let filter = getFlag(options, keys, 'filter', mustBeRegExp)\n          let namespace = getFlag(options, keys, 'namespace', mustBeString)\n          checkForInvalidFlags(options, keys, `in onResolve() call for plugin ${quote(name)}`)\n          if (filter == null) throw new Error(`onResolve() call is missing a filter`)\n          let id = nextCallbackID++\n          onResolveCallbacks[id] = { name: name!, callback, note: registeredNote }\n          plugin.onResolve.push({ id, filter: jsRegExpToGoRegExp(filter), namespace: namespace || '' })\n        },\n\n        onLoad(options, callback) {\n          let registeredText = `This error came from the \"onLoad\" callback registered here:`\n          let registeredNote = extractCallerV8(new Error(registeredText), streamIn, 'onLoad')\n          let keys: OptionKeys = {}\n          let filter = getFlag(options, keys, 'filter', mustBeRegExp)\n          let namespace = getFlag(options, keys, 'namespace', mustBeString)\n          checkForInvalidFlags(options, keys, `in onLoad() call for plugin ${quote(name)}`)\n          if (filter == null) throw new Error(`onLoad() call is missing a filter`)\n          let id = nextCallbackID++\n          onLoadCallbacks[id] = { name: name!, callback, note: registeredNote }\n          plugin.onLoad.push({ id, filter: jsRegExpToGoRegExp(filter), namespace: namespace || '' })\n        },\n","sourceCodeStart":1292,"sourceCodeEnd":1328,"githubUrl":"https://github.com/evanw/esbuild/blob/6ff1d8b0d8c134e867a397eef39702a223ebef9e/lib/shared/common.ts#L1292-L1328","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a RegExp: build.onResolve({ filter: /\\.css$/ }, cb).","Use /.*/  only as a last resort (it disables the perf optimisation); prefer a tight pattern.","Remember the regex is matched against the unresolved path; escape dots: /\\.css$/ not /.css$/."],"exampleFix":"// before\nbuild.onResolve({ filter: '*.css' }, args => { /* ... */ });\n// after\nbuild.onResolve({ filter: /\\.css$/ }, args => { /* ... */ });","handlingStrategy":"type-guard","validationCode":"function registerOnResolve(build, opts, cb) {\n  if (!(opts?.filter instanceof RegExp)) {\n    throw new TypeError('onResolve requires options.filter to be a RegExp');\n  }\n  build.onResolve(opts, cb);\n}","typeGuard":"function hasRegExpFilter(o): o is { filter: RegExp } {\n  return !!o && o.filter instanceof RegExp;\n}","tryCatchPattern":null,"preventionTips":["Always pass filter as a RegExp literal — never a glob string.","Scope the filter tightly for performance; /.*/  is a last resort.","Escape literal dots: /\\.css$/, not /.css$/."],"tags":["plugins","onresolve","regex","validation"],"analyzedSha":"6ff1d8b0d8c134e867a397eef39702a223ebef9e","analyzedAt":"2026-08-03T19:42:38.433Z","schemaVersion":2}