Vite "Failed to resolve import" (tested)
Vite (and the Rollup bundler it uses for vite build) throws this error whenever a JavaScript import statement points at something it cannot find on disk or in node_modules. The exact wording depends on whether you're running the dev server or building, and on whether the import is a package name, an alias, or a relative path. We reproduced all three causes against a real project and recorded the exact text.
1. The two error messages you'll see
Vite reports unresolved imports differently depending on the command:
- Dev server (
vite/npm run dev):Failed to resolve import "X" from "Y". Does the file exist?, shown in the browser's error overlay and in the terminal. - Build (
vite build): either[vite]: Rollup failed to resolve import "X" from "Y"(for bare package names and aliases) or the plainerCould not resolve "X" from "Y"(for relative paths), printed in the terminal and failing the build.
Both mean the same thing: Vite's resolver walked the import specifier and found nothing at the end of it. The fix depends on which of the three causes below triggered it.
2. Our test setup
We created a minimal project: a bare package.json with only vite as a dev dependency, an index.html, and src/main.js/src/Button.js/src/components/Foo.js. No framework, no plugins. We ran npx vite build for the build-mode errors and npx vite (fetching the broken module over HTTP) for the dev-mode errors, swapping one broken import into src/main.js at a time.
3. Cause 1: importing a package that isn't installed
Code: import x from 'totally-fake-pkg-xyz'; — the package was never npm installed.
Build mode exact error:
error during build:
[vite]: Rollup failed to resolve import "totally-fake-pkg-xyz" from "/tmp/vite-resolve-guide/src/main.js".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
Dev server exact error (from the error overlay JSON payload):
Failed to resolve import "totally-fake-pkg-xyz" from "src/main.js". Does the file exist?
Fix: install it (npm install totally-fake-pkg-xyz) or, if the name is wrong, correct the import to an installed package. If it really should be loaded from a <script> tag at runtime instead of bundled, add it to build.rollupOptions.external as the build-mode message suggests — but that's rarely what you want for a plain typo.
4. Cause 2: a path alias used in code but not configured
Code: import Foo from '@/components/Foo'; with no resolve.alias entry for @ in vite.config.js. The file src/components/Foo.js exists; only the alias is missing.
Build mode exact error: identical shape to the missing-package case, just with the alias path as the specifier:
error during build:
[vite]: Rollup failed to resolve import "@/components/Foo" from "/tmp/vite-resolve-guide/src/main.js".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
That matters: Vite's resolver treats an unconfigured alias exactly like a missing npm package, because @/components/Foo isn't a relative path (no ./ or ../) and isn't a package in node_modules either, so it falls into the same "bare specifier" bucket.
The fix, measured to work: add the alias to vite.config.js.
import path from 'node:path';
export default {
resolve: {
alias: {
'@': path.resolve(process.cwd(), 'src'),
},
},
};
After adding it, the same import built cleanly: vite build went from a hard failure to ✓ built in 77ms with no code changes other than the config. If you use TypeScript, also add the matching entry to compilerOptions.paths in tsconfig.json so the editor and type-checker agree with Vite — that part is per the Vite docs, not measured here.
5. Cause 3: a typo'd filename in a relative import
Code: import Button from './Buton'; — src/Button.js exists, but the import has a typo (missing second t) and no extension.
Build mode exact error — this is a different message shape than causes 1 and 2, with no externalize suggestion:
error during build:
Could not resolve "./Buton" from "src/main.js"
file: /tmp/vite-resolve-guide/src/main.js
Dev server exact error — same wording style as the missing-package case:
Failed to resolve import "./Buton" from "src/main.js". Does the file exist?
The dev server overlay also included a code frame pointing at the exact column of the import:
1 | import Button from './Buton';
| ^
2 | console.log("hello", Button);
We observed no "Did you mean './Button'?" suggestion in either mode, even though Button.js sits right next to the typo'd file. Don't assume Vite will catch close typos for you — it only tells you the path didn't resolve, not what you probably meant. Double-check capitalization and spelling by hand, or let your editor's autocomplete write the import path for you.
6. Dev server vs. build: why the wording differs
| Situation | Dev server (vite) | Build (vite build) |
|---|---|---|
| Missing package | Failed to resolve import "X" from "Y". Does the file exist? | [vite]: Rollup failed to resolve import "X" from "Y" + externalize hint |
| Unconfigured alias | same "Does the file exist?" form | same Rollup + externalize hint form |
| Typo'd relative path | same "Does the file exist?" form | Could not resolve "X" from "Y" (no externalize hint) |
In other words: in dev mode every unresolved import gets the same "Does the file exist?" phrasing regardless of cause. In build mode, Rollup splits them — bare specifiers (packages and aliases) get the longer message suggesting build.rollupOptions.external, while relative paths get the shorter "Could not resolve" message. If you only ever test in dev and ship straight to production, the build failure's wording will look unfamiliar the first time you see it.
7. Checklist
- Read the specifier in the error exactly as written.
@/...andbare-nameboth mean "not a relative path" to Vite's resolver, even if you intended one as an alias. - If it's a real package:
npm installit, or fix a misspelled package name. - If it's an alias (
@/,~/, etc.): add it underresolve.aliasinvite.config.js, and mirror it intsconfig.jsonpathsif you use TypeScript (per the Vite/TS docs). - If it's a relative path: check spelling and case by hand. We measured no "did you mean" hint from Vite for a one-letter typo.
- Restart the dev server after editing
vite.config.js. Config changes aren't picked up by hot reload. - Run
vite buildlocally before deploying. An import that happens to resolve in dev (e.g. because of a stray file) can still fail at build time, and the error text will look different than what you saw in the browser overlay.
Frequently asked questions
What does "Failed to resolve import X from Y. Does the file exist?" mean?
It's Vite dev-server's wording for any import it can't find: a missing npm package, an unconfigured path alias, or a typo'd relative path. We measured the identical phrasing for all three causes in dev mode; only the build-mode error text tells them apart.
Why does vite build give a different error than the dev server for the same broken import?
Because vite build runs Rollup's resolver, which separates bare specifiers (packages, aliases) from relative paths. We measured Rollup's message for a missing package and an unconfigured alias as "[vite]: Rollup failed to resolve import ... add it to build.rollupOptions.external", while a typo'd relative path got the shorter "Could not resolve X from Y" with no such hint.
Does Vite suggest the correct filename for a typo, like "Did you mean './Button'?"
No, not in our test. We imported './Buton' when './Button.js' existed in the same folder, in both dev and build mode, and got only "file doesn't exist" style errors with no spelling suggestion.
I configured an alias but Vite still can't resolve it. Why?
Common reasons: the dev server wasn't restarted after editing vite.config.js (config changes don't hot-reload), the alias key doesn't match what's used in the import, or (for TypeScript) tsconfig.json's compilerOptions.paths doesn't mirror the Vite alias, which satisfies the editor but not Vite's own resolver or vice versa.
Does build.rollupOptions.external actually fix a missing import?
It stops Rollup from bundling that specifier and leaves the import in the output as-is, which only works if something else supplies that module at runtime (e.g. a script tag or Node's own resolution). For an ordinary typo or a package you forgot to install, the real fix is to correct the import or run npm install, not to externalize it.
Written with AI assistance. Measured on 2026-10-02 with Vite 5.4.21 (Rollup bundled with it) and Node.js 24.21.0, via a minimal vanilla-JS project (no framework) built with `npx vite build` and served with `npx vite` (dev server). PlainHeaders is our extension; curl and Node.js are free.