{"id":"5acb7b54c9f5e586","repo":"evanw/esbuild","slug":"plugin-at-index-i-must-be-an-object","errorCode":null,"errorMessage":"Plugin at index ${i} must be an object","messagePattern":"Plugin at index (.+?) must be an object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/shared/common.ts","lineNumber":1222,"sourceCode":"      name: string,\n      note: () => types.Note | undefined,\n      callback: (args: types.OnLoadArgs) =>\n        (types.OnLoadResult | null | undefined | Promise<types.OnLoadResult | null | undefined>),\n    },\n  } = {}\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')","sourceCodeStart":1204,"sourceCodeEnd":1240,"githubUrl":"https://github.com/evanw/esbuild/blob/6ff1d8b0d8c134e867a397eef39702a223ebef9e/lib/shared/common.ts#L1204-L1240","documentation":"esbuild's build plugins array must be a list of plugin objects, each with at least a name and a setup function. At lib/shared/common.ts:1222 the loop over plugins checks typeof item === 'object' first; if an entry is a primitive (string, number), null, or a function, the check fails and esbuild throws with the offending index. This is a hard structural validation before name/setup are even inspected.","triggerScenarios":"Passing plugins: 'myPlugin' (a string) or plugins: [someFunction] instead of an array of objects. Passing plugins: [{ name:'a', setup:fn }, null] (trailing null from a bad filter). Passing a single plugin object that isn't wrapped in an array (gets iterated by char if it's a string, or throws immediately).","commonSituations":"Conditional plugins: const plugins = [cond && {name,setup}]; the && yields false when cond is false, putting false into the array. Spreading a possibly-undefined list: plugins: [...basePlugins, maybePlugin] where maybePlugin is undefined. Forgetting to wrap: plugins: myPluginObject.","solutions":["Ensure every element of the plugins array is a plain object: filter(Boolean) before passing: plugins: [a, b].filter(Boolean).","Wrap a single plugin in an array: plugins: [myPlugin].","Add a TS type annotation plugins: Plugin[] so the compiler rejects non-objects."],"exampleFix":"// before\nesbuild.build({\n  plugins: [vuePlugin, useReact && reactPlugin], // reactPlugin is `false` when disabled\n});\n// after\nesbuild.build({\n  plugins: [vuePlugin, useReact && reactPlugin].filter(Boolean) as Plugin[],\n});","handlingStrategy":"validation","validationCode":"function cleanPlugins(input) {\n  return (Array.isArray(input) ? input : [input])\n    .filter((p): p is { name: string; setup: Function } =>\n      !!p && typeof p === 'object' && typeof p.name === 'string' && typeof p.setup === 'function');\n}\nawait esbuild.build({ ...opts, plugins: cleanPlugins(opts.plugins) });","typeGuard":"function isPluginObject(v): v is { name: string; setup: Function } {\n  return !!v && typeof v === 'object' && !Array.isArray(v)\n    && typeof v.name === 'string' && v.name !== ''\n    && typeof v.setup === 'function';\n}","tryCatchPattern":null,"preventionTips":["Type your plugins array as Plugin[] so the compiler catches non-objects.","Always .filter(Boolean) when conditionally including plugins.","Wrap a single plugin in an array literal."],"tags":["plugins","validation","api-misuse"],"analyzedSha":"6ff1d8b0d8c134e867a397eef39702a223ebef9e","analyzedAt":"2026-08-03T19:42:38.433Z","schemaVersion":2}