← Debugging Guides

阅读中文版本

Vite / Webpack — Module not found

Diagnose unresolved imports caused by case sensitivity, aliases, package exports, missing dependencies, or the wrong working directory, and fix resolution without disabling checks.

beginnerdebuggingjavascriptbuild-tools

Symptoms

  • Failed to resolve import "./Button" from "src/App.tsx"
  • Module not found: Can't resolve 'lib/utils'
  • The import works on macOS but fails on Linux CI
  • The package is installed, but the bundler cannot resolve a subpath

Possible causes

  • The file name case or extension differs from the import path
  • A path alias is configured in tsconfig but not in the bundler, or the reverse
  • The dependency is missing from package.json or installed at a different workspace level
  • The package exports field does not expose the requested subpath
  • The import is resolved relative to the wrong working directory or root

Diagnosis steps

Reproduce with the bundler's resolution debug output

npx vite --debug 2>&1 | grep -i 'resolve\|failed'

Interpretation

Vite prints the importer and the attempted resolution. Webpack users can run with --stats detailed or inspect the resolve configuration.

Verify the dependency and its exported paths

npm ls PACKAGE && node -e "console.log(require.resolve('PACKAGE'))"

Interpretation

A missing package or an exports map that blocks the subpath explains a resolution failure despite an installed dependency.

Compare the import path with the actual filename

find src -iname 'Button*'

Interpretation

Case-only differences work on a case-insensitive filesystem and fail on Linux. The build must match the exact case.

Root cause

The bundler cannot map the import specifier to a file. The cause is a path mismatch, an alias that exists in only one configuration, a missing dependency, or a package exports restriction.

Fix

Correct the path and case, add the alias to both tsconfig and the bundler, install the dependency at the correct workspace level, or import a subpath the package actually exports. Do not disable resolution checks or add broad fallbacks.

Risk note

A catch-all alias or a custom resolver that ignores exports can load the wrong file or bypass package boundaries, producing runtime bugs that the build no longer catches.

Verify the fix

Run a clean install and build in a Linux-like environment, then confirm the bundle resolves the import and the page renders. Add a CI build on a case-sensitive filesystem so local macOS behavior cannot hide the issue.

The error names the importer, not always the cause

Failed to resolve import "./Button" usually means the file is not where the import says, but the mismatch can be subtle: case, extension, an alias resolved differently by TypeScript and the bundler, or a package subpath blocked by exports. The import works on macOS because the filesystem is case-insensitive; Linux CI fails on Button versus button.

src/components/button.tsx
import { Button } from './Button'; // works on macOS, fails on Linux

Run the bundler with resolution debugging and compare the attempted path with find src -iname. Fix the source path to the exact case, or rename the file consistently.

Aliases must agree

If @/components is configured in tsconfig.json but not in vite.config.ts, the editor passes and the build fails. Configure the alias in both places from one source, or use a relative import. The same applies to Jest, Vitest, and any server-side runtime that resolves modules separately.

// vite.config.ts
resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } }

Check package exports

A package may be installed but not expose the subpath you import. exports maps are authoritative for modern Node and bundlers. import 'pkg/internal/foo' can fail even when the file exists in node_modules. Import a documented entry point or add the subpath through the package’s public API. Do not bypass exports with a deep relative path into node_modules.

Fix and verify

Correct the path, alias, dependency, or import target. Run a clean install and build in a Linux-like environment or CI, then confirm the bundle contains the module and the page renders. A local macOS build alone is not evidence because it does not exercise case-sensitive resolution. Do not disable resolution checks or add broad fallbacks; they hide the real mismatch and can load the wrong implementation.