evanw/esbuild · error · Error

Plugin at index is missing a name

Error message

Plugin at index ${i} is missing a name

What it means

During plugin validation (lib/shared/common.ts:1228), esbuild reads the `name` field with `mustBeString` and additionally rejects the empty string. A plugin must have a non-empty `name`, used in error messages and for resolving plugin-specific behavior.

Solutions

  1. Add a non-empty `name` string to every plugin object.
  2. If importing the plugin, verify the export includes a `name` property.
  3. Run a type guard over the plugins array before calling build (see defense section).

Example fix

// before
{ setup(build) { /* ... */ } }
// after
{ name: 'my-plugin', setup(build) { /* ... */ } }
Defensive patterns

Strategy: type-guard

Validate before calling

function ensurePluginNames(plugins: any[]): void {
  plugins.forEach((p, i) => {
    if (typeof p?.name !== 'string' || p.name === '') throw new Error(`Plugin at index ${i} is missing a name`);
  });
}

Type guard

function pluginHasName(p: any): p is { name: string } {
  return typeof p === 'object' && p !== null && typeof p.name === 'string' && p.name.length > 0;
}

Prevention

When it happens

Trigger: Passing a plugin object whose `name` is missing, undefined, an empty string, or a non-string type — e.g. `{ setup() {} }` or `{ name: '', setup() {} }`.

Common situations: Forgetting the `name` field when authoring a quick plugin; a plugin object built dynamically where the name key was typo'd or conditionally omitted; default-export interop returning an object without `name`.

Related errors


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

Appendix: source

Thrown at lib/shared/common.ts:1228

      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 f6058f8364)