{"id":"50c9e15bb7c2cdee","repo":"vitejs/vite","slug":"external-must-be-an-array","errorCode":null,"errorMessage":"external must be an array","messagePattern":"external must be an array","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vite/rolldown.config.ts","lineNumber":215,"sourceCode":"          \"export * from '../../src/node/index.ts'\",\n        )\n        writeFileSync(\n          'dist/node/module-runner.d.ts',\n          \"export * from '../../src/module-runner/index.ts'\",\n        )\n      }\n    },\n  }\n}\n\nfunction externalizeDepsInWatchPlugin(): Plugin {\n  return {\n    name: 'externalize-deps-in-watch',\n    options(options) {\n      if (this.meta.watchMode) {\n        options.external ||= []\n        if (!Array.isArray(options.external))\n          throw new Error('external must be an array')\n        options.external = options.external.concat(\n          Object.keys(pkg.devDependencies),\n        )\n      }\n    },\n  }\n}\n\ninterface ShimOptions {\n  src?: string\n  replacement: string\n  pattern?: RegExp\n}\n\nfunction shimDepsPlugin(deps: Record<string, ShimOptions[]>): Plugin {\n  const transformed: Record<string, boolean> = {}\n\n  return {","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/vitejs/vite/blob/89620f09afcfef6b35e7bb8660132ab5b4d0cd3b/packages/vite/rolldown.config.ts#L197-L233","documentation":"Thrown by the externalizeDepsInWatchPlugin in Vite's own rolldown build config when running in watch mode. The plugin attempts to concatenate devDependencies onto options.external, but Rollup/Rolldown allows external to be a function (not just an array). If the resolved config's external is a function or a non-array value, this assertion fires. This error is internal to building Vite itself, not consumer application builds.","triggerScenarios":"Running Vite's own build in watch mode (e.g., pnpm dev or the rolldown build with --watch) when the nodeConfig's external field has been changed from an array to a function or another type by a code change in rolldown.config.ts.","commonSituations":"A contributor to Vite's codebase modifies the external field in nodeConfig to use a function for more sophisticated externals resolution, then runs the dev/watch build. This is a development-time error for Vite contributors, not end users.","solutions":["If you modified nodeConfig.external to a function, convert the externalizeDepsInWatchPlugin logic to handle function externals (wrap the function).","Keep options.external as an array in the base config; the watch-mode plugin only supports arrays.","If you hit this without modifying Vite's source, ensure you're using the correct build scripts."],"exampleFix":"// before — external is a function\nexternal: (id) => id.includes('node:')\n// after — the watch plugin needs an array; wrap instead\nconst baseExternal = [...Object.keys(pkg.dependencies)]\nexternal: [\n  ...baseExternal,\n  // add function-based externals as regex patterns instead\n]","handlingStrategy":"validation","validationCode":"// For Vite contributors: validate external is an array before watch-mode concatenation\nfunction ensureArrayExternal(options) {\n  if (options.external && !Array.isArray(options.external)) {\n    throw new TypeError('options.external must be an array for watch-mode devDependencies injection')\n  }\n}","typeGuard":"function isArrayExternal(external: unknown): external is string[] | RegExp[] {\n  return Array.isArray(external)\n}","tryCatchPattern":"// In the plugin, handle function externals gracefully\nif (!Array.isArray(options.external)) {\n  if (typeof options.external === 'function') {\n    const fn = options.external\n    const deps = Object.keys(pkg.devDependencies)\n    options.external = (id) => fn(id) || deps.includes(id)\n  } else {\n    throw new Error('external must be an array')\n  }\n}","preventionTips":["When contributing to Vite, keep nodeConfig.external as an array, not a function.","If you need function-based externals, modify externalizeDepsInWatchPlugin to wrap them.","Run the watch build after any external config changes."],"tags":["rolldown-config","watch-mode","external","internal-vite-build"],"analyzedSha":"89620f09afcfef6b35e7bb8660132ab5b4d0cd3b","analyzedAt":"2026-08-03T19:28:02.920Z","schemaVersion":2}