vitejs/vite · error · Error

Package subpath '${relativeId}' is not defined by "exports"

Error message

Package subpath '${relativeId}' is not defined by "exports" in ${path.join(dir, 'package.json')}.

What it means

Vite's resolver honors a package's package.json "exports" map (Node's subpath resolution). When a dependency declares an "exports" field but the deep-import path you requested matches none of its entries, Vite refuses to fall back to the filesystem and throws at packages/vite/src/node/plugins/resolve.ts:1081. This mirrors Node's own ERR_PACKAGE_PATH_NOT_EXPORTED so bundler and runtime resolution agree.

Source

Thrown at packages/vite/src/node/plugins/resolve.ts:1081

      const { file, postfix } = splitFileAndPostfix(relativeId)
      const exportsId = resolveExportsOrImports(
        data,
        file,
        options,
        'exports',
        externalize,
      )
      if (exportsId !== undefined) {
        relativeId = exportsId + postfix
      } else {
        relativeId = undefined
      }
    } else {
      // not exposed
      relativeId = undefined
    }
    if (!relativeId) {
      throw new Error(
        `Package subpath '${relativeId}' is not defined by "exports" in ` +
          `${path.join(dir, 'package.json')}.`,
      )
    }
  } else if (options.mainFields.includes('browser') && isObject(browserField)) {
    // resolve without postfix (see #7098)
    const { file, postfix } = splitFileAndPostfix(relativeId)
    const mapped = mapWithBrowserField(file, browserField)
    if (mapped) {
      relativeId = mapped + postfix
    } else if (mapped === false) {
      setResolvedCache(id, browserExternalId, options)
      return browserExternalId
    }
  }

  if (relativeId) {
    const resolved = tryFsResolve(

View on GitHub (pinned to 89620f09af)

Solutions

  1. Read the offending package's package.json exports field and import only a path that is actually listed (usually the bare package name or a documented subpath).
  2. If you control the package, add the subpath to its exports map (e.g. "./internal/util": "./src/internal/util.ts").
  3. Pin or downgrade the dependency to a version whose exports still allowed the deep import, if you cannot change the import.
  4. If you must bypass exports, alias the path in vite.config resolve.alias and resolve.conditions so Vite resolves the real file directly.

Example fix

// before
import { escape } from 'lodash/escape'

// after (lodash-es exposes only '.'; use the documented entry)
import { escape } from 'lodash-es'
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'node:fs'
import { createRequire } from 'node:module'

function subpathIsExported(pkgDir: string, subpath: string): boolean {
  const pkg = JSON.parse(readFileSync(`${pkgDir}/package.json`, 'utf8'))
  if (!pkg.exports) return true // no exports field => fs resolution applies
  const keys = Object.keys(pkg.exports)
  return keys.includes(subpath) || keys.includes('./' + subpath.replace(/^\.?\//, ''))
}
// before importing 'lib/internal/util':
if (!subpathIsExported(require.resolve('lib').replace(/package\.json$/, '..'), './internal/util')) {
  throw new Error('refusing to import a subpath not in package exports')
}

Type guard

function isExportedSubpath(exportsField: unknown, subpath: string): boolean {
  if (!exportsField || typeof exportsField !== 'object') return true
  return Object.prototype.hasOwnProperty.call(exportsField, subpath)
}

Try / catch

try {
  await import('lib/internal/util')
} catch (e) {
  if (/is not defined by "exports"/.test((e as Error).message)) {
    // fall back to a documented entry point
    return await import('lib')
  }
  throw e
}

Prevention

When it happens

Trigger: Importing a subpath that the package author did not expose, e.g. import x from 'lib/internal/util' when lib's package.json only exports '.' and './feature'. Also triggered by query/postfix-suffixed ids ('lib/dist/index.js?sourcemap') after splitFileAndPostfix fails to match, or by importing a path that only existed pre-exports (lib/package.json versions that added a restrictive exports field).

Common situations: Upgrading a dependency that introduced/ tightened its exports field (e.g. lodash-es, @vue/runtime-core, react-router); tooling (postcss, autoprefixer plugins) importing deep internal files; monorepo workspace packages whose exports forgot to list a subpath; using a build of a lib whose exports only point at ESM while you import CJS-only internals.

Related errors


AI-assisted analysis of vitejs/vite@89620f09af (2026-08-03). Data as JSON: /data/errors/9d2e64769ea941e4.json. Report an issue: GitHub.