{"record":{"id":"58408af54fc05cc4","repo":"evanw/esbuild","slug":"plugin-at-index-i-is-missing-a-name","errorCode":null,"errorMessage":"Plugin at index ${i} is missing a name","messagePattern":"Plugin at index (.+?) is missing a name","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/shared/common.ts","lineNumber":1228,"sourceCode":"      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')\n        if (typeof path !== 'string') throw new Error(`The path to resolve must be a string`)\n        let keys: OptionKeys = Object.create(null)","sourceCodeStart":1210,"sourceCodeEnd":1246,"githubUrl":"https://github.com/evanw/esbuild/blob/f6058f8364fe7ab91ca57a83e02577ed74c9cae4/lib/shared/common.ts#L1210-L1246","documentation":"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.","triggerScenarios":"Passing a plugin object whose `name` is missing, undefined, an empty string, or a non-string type — e.g. `{ setup() {} }` or `{ name: '', setup() {} }`.","commonSituations":"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`.","solutions":["Add a non-empty `name` string to every plugin object.","If importing the plugin, verify the export includes a `name` property.","Run a type guard over the plugins array before calling build (see defense section)."],"exampleFix":"// before\n{ setup(build) { /* ... */ } }\n// after\n{ name: 'my-plugin', setup(build) { /* ... */ } }","handlingStrategy":"type-guard","validationCode":"function ensurePluginNames(plugins: any[]): void {\n  plugins.forEach((p, i) => {\n    if (typeof p?.name !== 'string' || p.name === '') throw new Error(`Plugin at index ${i} is missing a name`);\n  });\n}","typeGuard":"function pluginHasName(p: any): p is { name: string } {\n  return typeof p === 'object' && p !== null && typeof p.name === 'string' && p.name.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Use a shared plugin factory that always injects a name.","Lint plugin definitions in CI to catch missing names."],"tags":["plugins","validation","config"],"backgroundTag":null,"analyzedSha":"f6058f8364fe7ab91ca57a83e02577ed74c9cae4","analyzedAt":"2026-08-09T18:37:22.223Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}