{"record":{"id":"93ae144094696b8f","repo":"vitejs/vite","slug":"vite-named-export-lastbinding-not-found-th","errorCode":null,"errorMessage":"[vite] Named export '${lastBinding}' not found. The requested module '${rawId}' is a CommonJS module, which may not support all module.exports as named exports.\nCommonJS modules can always be imported via the default export, for example using:\n\nimport pkg from '${rawId}';\nconst {${missingBindings.join(', ')}} = pkg;\n","messagePattern":"\\[vite\\] Named export '(.+?)' not found\\. The requested module '(.+?)' is a CommonJS module, which may not support all module\\.exports as named exports\\.\nCommonJS modules can always be imported via the default export, for example using:\n\nimport pkg from '(.+?)';\nconst (.+?) = pkg;\n","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"packages/vite/src/shared/ssrTransform.ts","lineNumber":49,"sourceCode":"\n  // If the user named imports a specifier that can't be analyzed, error.\n  // If the module doesn't import anything explicitly, e.g. `import 'foo'` or\n  // `import * as foo from 'foo'`, we can skip.\n  if (metadata?.importedNames?.length) {\n    const missingBindings = metadata.importedNames.filter((s) => !(s in mod))\n    if (missingBindings.length) {\n      const lastBinding = missingBindings[missingBindings.length - 1]\n\n      // For invalid named exports only, similar to how Node.js errors for top-level imports.\n      // But since we transform as dynamic imports, we need to emulate the error manually.\n      if (moduleType === 'module') {\n        throw new SyntaxError(\n          `[vite] The requested module '${rawId}' does not provide an export named '${lastBinding}'`,\n        )\n      } else {\n        // For non-ESM, named imports is done via static analysis with cjs-module-lexer in Node.js.\n        // Copied from Node.js\n        throw new SyntaxError(`\\\n[vite] Named export '${lastBinding}' not found. The requested module '${rawId}' is a CommonJS module, which may not support all module.exports as named exports.\nCommonJS modules can always be imported via the default export, for example using:\n\nimport pkg from '${rawId}';\nconst {${missingBindings.join(', ')}} = pkg;\n`)\n      }\n    }\n  }\n}\n","sourceCodeStart":31,"sourceCodeEnd":60,"githubUrl":"https://github.com/vitejs/vite/blob/b4d66fee14d970f45b8a6f3d7d6aee73ca9b88ab/packages/vite/src/shared/ssrTransform.ts#L31-L60","documentation":"This is a Vite SSR-only error thrown by analyzeImportedModDifference when a named import (e.g. `import { foo } from 'pkg'`) targets a CommonJS module whose `module.exports` does not statically expose that binding. Vite transforms SSR imports behind the scenes and, unlike Node.js, cannot always rely on cjs-module-lexer at runtime, so it manually emulates Node's native 'Named export not found' SyntaxError to surface the mismatch early. The guard fires only for non-ESM modules; for true ESM it throws the Node-style 'does not provide an export named' variant instead. It exists to fail fast in dev SSR rather than silently yielding `undefined` bindings.","triggerScenarios":"Triggered when `metadata.importedNames` is a non-empty array (i.e. the user wrote named imports, not `import * as` or a bare side-effect import), the resolved module is NOT flagged `moduleType === 'module'` (it is CJS), and `metadata.importedNames.filter((s) => !(s in mod))` returns at least one missing binding. Concretely: `import { debounce } from 'lodash'` (CJS lodash, no static named export detected), `import { Provider } from 'some-cjs-pkg'` where the package assigns `module.exports = { Provider }` but isn't analyzed, or any deep named import from a `.cjs`/UMD package under `ssr` mode. Dynamic imports (`import()`) are exempt: the function returns early when `metadata?.isDynamicImport` is true.","commonSituations":"Most common after enabling SSR (or adding `ssr.noExternal: false`) on a project that imports legacy CJS/UMD libraries via named imports — lodash, moment, classnames, qs, rxjs pre-v7, older react-redux, internal company packages shipped as CJS. Also surfaces after upgrading Vite/Rollup across versions where cjs-module-lexer detection regressed, after adding `optimizeDeps.exclude` for a CJS dep, or when a package.json `exports`/`type` field flips a module between ESM and CJS. Misconfigured `ssr.external` or pointing a named import at a package whose `main` field points at a CJS build while `module`/`exports` point at ESM is a frequent cause.","solutions":["Switch to the default-import pattern the error message itself suggests: `import pkg from 'pkg'; const { foo } = pkg;` — this sidesteps static named-export analysis entirely.","Force the dependency through Vite's optimizer so named exports are pre-bundled as ESM: add it to `optimizeDeps.include` (and confirm `optimizeDeps.exclude` does not list it).","If the package ships an ESM build, point your import at it explicitly (subpath or the `module`/`exports` entry), or upgrade to an ESM-native version (e.g. lodash-es, rxjs v7+).","For SSR specifically, mark the package as non-external so Vite transforms it: add the name to `ssr.noExternal` (string, regex, or `true` to transform everything).","If you control the CJS package, add named-export hints cjs-module-lexer can detect (`exports.foo = ...` or `Object.defineProperty(exports, 'foo', ...)`) instead of `module.exports = { foo }` assigned all at once.","Use a namespace import `import * as pkg from 'pkg'` — the early-return at line 35 (`importedNames?.length` falsy / undefined for `import * as`) skips the check entirely."],"exampleFix":"// before\nimport { debounce } from 'lodash'\n\n// after (default import + destructure, as the error suggests)\nimport lodash from 'lodash'\nconst { debounce } = lodash\n\n// or: force ESM pre-bundle in vite.config.ts\n// export default defineConfig({\n//   optimizeDeps: { include: ['lodash'] },\n//   ssr: { noExternal: ['lodash'] }\n// })\n\n// or: use the ESM build\nimport { debounce } from 'lodash-es'","handlingStrategy":"validation","validationCode":"// Before relying on a named import from a possibly-CJS dep in SSR,\n// check whether Vite resolved it as ESM. Run in vite config or a plugin.\nimport { createServer } from 'vite'\n\nconst server = await createServer({ server: { middlewareMode: true } })\nconst id = await server.pluginContainer.resolveId('pkg-name')\nif (id) {\n  const mod = await server.ssrLoadModule('pkg-name').catch(() => null)\n  // introspect: is the binding actually exported?\n  const ok = mod != null && 'theBinding' in mod\n  if (!ok) {\n    // fall back to default import or pre-bundle via optimizeDeps.include\n  }\n}\nawait server.close()\n\n// Cheaper static check: read the dep's package.json before adding the import\nimport { readFileSync } from 'node:fs'\nconst pkg = JSON.parse(readFileSync('node_modules/pkg-name/package.json', 'utf8'))\nconst isEsm = pkg.type === 'module' || (pkg.exports && typeof pkg.exports === 'object')\n// if !isEsm, prefer `import pkg from 'pkg-name'` over named imports","typeGuard":"// Narrow a dynamically-imported CJS module to a known shape before destructuring.\nimport type { ExpectedNamedExports } from './types'\n\nfunction hasNamedExports(\n  mod: unknown,\n  names: readonly (keyof ExpectedNamedExports)[],\n): mod is ExpectedNamedExports {\n  if (typeof mod !== 'object' || mod === null) return false\n  return names.every((n) => n in mod)\n}\n\n// usage in SSR code that cannot statically prove ESM:\n// const raw = await import('maybe-cjs-pkg')\n// const mod = (raw as any).default ?? raw\n// if (!hasNamedExports(mod, ['foo', 'bar'] as const)) {\n//   throw new Error('maybe-cjs-pkg is missing expected named exports')\n// }","tryCatchPattern":"// This is a SyntaxError thrown synchronously during SSR module load;\n// catching it at the call site is rarely useful — it means the import\n// is fundamentally broken. Prefer validation (above) or fixing the import.\n// If you must guard a dynamic SSR pipeline, catch around ssrLoadModule:\ntry {\n  const mod = await viteServer.ssrLoadModule('./src/uses-cjs-pkg.ts')\n} catch (err) {\n  if (err instanceof SyntaxError && /Named export .* not found/.test(err.message)) {\n    // log + surface a friendly 'switch to default import' hint to the user\n    throw new Error('Configure optimizeDeps.include or use default import for the failing package')\n  }\n  throw err\n}","preventionTips":["Audit every named import against CJS dependencies before enabling SSR; default-import or namespace-import CJS packages instead.","Keep `optimizeDeps.include` populated for any CJS package your app imports by name so Vite pre-bundles ESM wrappers.","Pin to ESM-native alternatives (lodash-es, rxjs >=7, etc.) and add a lint rule banning CJS-only package names from named imports.","When publishing a CJS package that will be consumed by Vite, assign named exports one-by-one (`exports.foo = ...`) rather than `module.exports = { foo }` so cjs-module-lexer can detect them.","Re-run the SSR build in CI with `optimizeDeps.exclude` empty; this surfaces CJS named-export mismatches before deploy.","Use `import * as pkg from 'pkg'` during migration as a stopgap — it bypasses the check at ssrTransform.ts:35 and lets you destructure at runtime."],"tags":["vite","ssr","esm","commonjs","esm-cjs-interop","import","build-tooling"],"backgroundTag":null,"analyzedSha":"b4d66fee14d970f45b8a6f3d7d6aee73ca9b88ab","analyzedAt":"2026-08-11T11:49:19.515Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}