{"record":{"id":"debda113a733eee3","repo":"evanw/esbuild","slug":"plugin-is-missing-a-setup-function","errorCode":null,"errorMessage":"Plugin is missing a setup function","messagePattern":"Plugin is missing a setup function","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/shared/common.ts","lineNumber":1231,"sourceCode":"  } = {}\n\n  let onDisposeCallbacks: (() => void)[] = []\n  let nextCallbackID = 0\n  let i = 0\n  let requestPlugins: protocol.BuildPlugin[] = []\n  let isSetupDone = false\n\n  // Clone the plugin array to guard against mutation during iteration\n  plugins = [...plugins]\n\n  for (let item of plugins) {\n    let keys: OptionKeys = {}\n    if (typeof item !== 'object') throw new Error(`Plugin at index ${i} must be an object`)\n    const name = getFlag(item, keys, 'name', mustBeString)\n    if (typeof name !== 'string' || name === '') throw new Error(`Plugin at index ${i} is missing a name`)\n    try {\n      let setup = getFlag(item, keys, 'setup', mustBeFunction)\n      if (typeof setup !== 'function') throw new Error(`Plugin is missing a setup function`)\n      checkForInvalidFlags(item, keys, `on plugin ${quote(name)}`)\n\n      let plugin: protocol.BuildPlugin = {\n        name,\n        onStart: false,\n        onEnd: false,\n        onResolve: [],\n        onLoad: [],\n      }\n      i++\n\n      let resolve = (path: string, options: types.ResolveOptions = {}): Promise<types.ResolveResult> => {\n        if (!isSetupDone) throw new Error('Cannot call \"resolve\" before plugin setup has completed')\n        if (typeof path !== 'string') throw new Error(`The path to resolve must be a string`)\n        let keys: OptionKeys = Object.create(null)\n        let pluginName = getFlag(options, keys, 'pluginName', mustBeString)\n        let importer = getFlag(options, keys, 'importer', mustBeString)\n        let namespace = getFlag(options, keys, 'namespace', mustBeString)","sourceCodeStart":1213,"sourceCodeEnd":1249,"githubUrl":"https://github.com/evanw/esbuild/blob/f6058f8364fe7ab91ca57a83e02577ed74c9cae4/lib/shared/common.ts#L1213-L1249","documentation":"During plugin validation (lib/shared/common.ts:1231), after confirming `name`, esbuild reads the `setup` field with `mustBeFunction` and throws if it is not a function. `setup` is the entry point esbuild invokes to register the plugin's hooks (onResolve, onLoad, onStart, onEnd, onDispose).","triggerScenarios":"Passing a plugin object whose `setup` is missing, undefined, or a non-function — e.g. `{ name: 'x', install() {} }` (wrong key) or `{ name: 'x' }`.","commonSituations":"Typo'ing the method name (`init`, `apply`, `install` instead of `setup`); copying a plugin skeleton that omitted the body; spreading defaults that don't include `setup`.","solutions":["Define a `setup(build)` function on the plugin object.","Fix any renamed key (e.g. `install` -> `setup`).","Validate the plugins array with a type guard before calling build."],"exampleFix":"// before\n{ name: 'x', install(build) { /* ... */ } }\n// after\n{ name: 'x', setup(build) { /* ... */ } }","handlingStrategy":"type-guard","validationCode":"function ensurePluginSetup(plugins: any[]): void {\n  plugins.forEach(p => { if (typeof p?.setup !== 'function') throw new Error(`Plugin ${p?.name} is missing a setup function`); });\n}","typeGuard":"function pluginHasSetup(p: any): p is { setup: Function } {\n  return typeof p === 'object' && p !== null && typeof p.setup === 'function';\n}","tryCatchPattern":null,"preventionTips":["Name the hook `setup` exactly — not install/init/apply.","Centralize plugin authoring in a typed helper to enforce the contract."],"tags":["plugins","validation","config"],"backgroundTag":null,"analyzedSha":"f6058f8364fe7ab91ca57a83e02577ed74c9cae4","analyzedAt":"2026-08-09T18:37:22.223Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}