evanw/esbuild · error · Error

Plugin at index must be an object

Error message

Plugin at index ${i} must be an object

What it means

During plugin setup (lib/shared/common.ts:1226), esbuild iterates the `plugins` array and checks each entry with `typeof item !== 'object'`. If an element is not an object (e.g. a string, number, null, or a function), this error is thrown. Each plugin must be a plain object literal with at least `name` and `setup`.

Solutions

  1. Wrap each plugin as an object with a `name` string and a `setup` function: `{ name: 'my-plugin', setup(build) { ... } }`.
  2. Check that your plugin factory/import actually returns the plugin object and not undefined or a function.
  3. Validate the array before passing it to esbuild (see type guard).

Example fix

// before
esbuild.build({ plugins: [myPluginSetup] });
// after
esbuild.build({ plugins: [{ name: 'my-plugin', setup: myPluginSetup }] });
Defensive patterns

Strategy: type-guard

Validate before calling

function validatePlugins(plugins: any[]): void {
  plugins.forEach((p, i) => {
    if (typeof p !== 'object' || p === null) throw new Error(`Plugin at index ${i} must be an object`);
  });
}
validatePlugins(options.plugins);

Type guard

function isPluginObject(v: any): v is { name: string; setup: Function } {
  return typeof v === 'object' && v !== null && typeof v.name === 'string' && v.name !== '' && typeof v.setup === 'function';
}

Try / catch

try {
  await esbuild.build(options);
} catch (e) {
  if (/Plugin at index .* must be an object/.test(String(e.message))) { /* fix plugins array */ }
  else throw e;
}

Prevention

When it happens

Trigger: Passing a `plugins` array whose element is a primitive, null, or a function instead of an object — e.g. `plugins: [myPluginFn]` or `plugins: ['some-name']` or `plugins: [null]`.

Common situations: Confusing esbuild's plugin API with other bundlers (e.g. Rollup/Webpack where plugins are sometimes constructor/function instances); spreading a single plugin instead of wrapping it; passing a plugin factory result that returned undefined.

Related errors


AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09). Data as JSON: /api/errors/5acb7b54c9f5e586. Report an issue: GitHub.

Appendix: source

Thrown at lib/shared/common.ts:1226

      name: string,
      note: () => types.Note | undefined,
      callback: (args: types.OnLoadArgs) =>
        (types.OnLoadResult | null | undefined | Promise<types.OnLoadResult | null | undefined>),
    },
  } = {}

  let onDisposeCallbacks: (() => void)[] = []
  let nextCallbackID = 0
  let i = 0
  let requestPlugins: protocol.BuildPlugin[] = []
  let isSetupDone = false

  // Clone the plugin array to guard against mutation during iteration
  plugins = [...plugins]

  for (let item of plugins) {
    let keys: OptionKeys = {}
    if (typeof item !== 'object') throw new Error(`Plugin at index ${i} must be an object`)
    const name = getFlag(item, keys, 'name', mustBeString)
    if (typeof name !== 'string' || name === '') throw new Error(`Plugin at index ${i} is missing a name`)
    try {
      let setup = getFlag(item, keys, 'setup', mustBeFunction)
      if (typeof setup !== 'function') throw new Error(`Plugin is missing a setup function`)
      checkForInvalidFlags(item, keys, `on plugin ${quote(name)}`)

      let plugin: protocol.BuildPlugin = {
        name,
        onStart: false,
        onEnd: false,
        onResolve: [],
        onLoad: [],
      }
      i++

      let resolve = (path: string, options: types.ResolveOptions = {}): Promise<types.ResolveResult> => {
        if (!isSetupDone) throw new Error('Cannot call "resolve" before plugin setup has completed')

View on GitHub (pinned to f6058f8364)