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
- Wrap each plugin as an object with a `name` string and a `setup` function: `{ name: 'my-plugin', setup(build) { ... } }`.
- Check that your plugin factory/import actually returns the plugin object and not undefined or a function.
- 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
- Always author plugins as object literals { name, setup }.
- Add a unit test that type-checks your plugins array before build.
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
- Must specify "kind" when calling "resolve"
- onLoad() call is missing a filter
- onResolve() call is missing a filter
- Plugin at index is missing a name
- Plugin is missing a setup function
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)