{"id":"1b80e995ad53fd1f","repo":"evanw/esbuild","slug":"onload-call-is-missing-a-filter","errorCode":null,"errorMessage":"onLoad() call is missing a filter","messagePattern":"onLoad\\(\\) call is missing a filter","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/shared/common.ts","lineNumber":1323,"sourceCode":"          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\n        onDispose(callback) {\n          onDisposeCallbacks.push(callback)\n        },\n\n        esbuild: streamIn.esbuild,\n      })\n\n      // Await a returned promise if there was one. This allows plugins to do\n      // some asynchronous setup while still retaining the ability to modify\n      // the build options. This deliberately serializes asynchronous plugin\n      // setup instead of running them concurrently so that build option\n      // modifications are easier to reason about.\n      if (promise) await promise","sourceCodeStart":1305,"sourceCodeEnd":1341,"githubUrl":"https://github.com/evanw/esbuild/blob/6ff1d8b0d8c134e867a397eef39702a223ebef9e/lib/shared/common.ts#L1305-L1341","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nbuild.onLoad({ namespace: 'mem' }, args => { /* ... */ });\n// after\nbuild.onLoad({ filter: /.*/, namespace: 'mem' }, args => { /* ... */ });","handlingStrategy":"type-guard","validationCode":"function registerOnLoad(build, opts, cb) {\n  if (!(opts?.filter instanceof RegExp)) {\n    throw new TypeError('onLoad requires options.filter to be a RegExp');\n  }\n  build.onLoad(opts, cb);\n}","typeGuard":"function hasLoadFilter(o): o is { filter: RegExp; namespace?: string } {\n  return !!o && o.filter instanceof RegExp;\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["plugins","onload","regex","validation"],"analyzedSha":"6ff1d8b0d8c134e867a397eef39702a223ebef9e","analyzedAt":"2026-08-03T19:42:38.433Z","schemaVersion":2}