evanw/esbuild · error · Error
Plugin at index ${i} is missing a name
Error message
Plugin at index ${i} is missing a name What it means
Every esbuild plugin must declare a non-empty name string so esbuild can attribute errors/warnings and route onResolve/onLoad callbacks. The check at lib/shared/common.ts:1224 fires when getFlag(name, mustBeString) yields undefined or the name is the empty string — both make the plugin unidentifiable. The name appears in error messages and in the resolve() pluginName option.
Source
Thrown at lib/shared/common.ts:1224
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')
if (typeof path !== 'string') throw new Error(`The path to resolve must be a string`)
let keys: OptionKeys = Object.create(null)View on GitHub (pinned to 6ff1d8b0d8)
Solutions
- Add name: 'my-plugin' (non-empty string) to every plugin object.
- In a plugin factory, default the name: function makePlugin(opts){ return { name: opts.name || 'my-plugin', setup } }.
- Add a unit test asserting all exported plugins have a non-empty string name.
Example fix
// before
export default {
setup(build) { /* ... */ },
};
// after
export default {
name: 'my-plugin',
setup(build) { /* ... */ },
}; Defensive patterns
Strategy: type-guard
Validate before calling
function withName(p) {
if (typeof p.name !== 'string' || p.name === '') {
throw new Error(`Plugin must have a non-empty string name; got ${JSON.stringify(p.name)}`);
}
return p;
} Type guard
function hasPluginName(p): p is { name: string } {
return typeof p?.name === 'string' && p.name.length > 0;
} Prevention
- Always include a name field when authoring plugins; use a factory that defaults it.
- Add a unit test that asserts every exported plugin has a non-empty name.
- Lint shared plugin code with a custom rule requiring name.
When it happens
Trigger: Plugin object missing the 'name' key entirely. name: '' or name: undefined. name: 123 (mustBeString returns null so getFlag yields undefined).
Common situations: Authoring a plugin from a factory that takes { setup } and forgets to assign a name. Copy-pasting a plugin template and deleting name. Dynamic plugins built via Object.assign that overwrite name with undefined.
Related errors
- Plugin at index ${i} must be an object
- Plugin is missing a setup function
- Missing "kind" in ${callName}() call
- Cannot call "resolve" before plugin setup has completed
- Must specify "kind" when calling "resolve"
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/58408af54fc05cc4.json.
Report an issue: GitHub.