Skip to content
Nilay Kabariya

Entry No.009·Fixes·Updated ·4 min read

Fix: Vite “Failed to resolve import” for @/ path aliases

Vite can't resolve @/ imports: why the build can pass while the dev server and Vitest fail, and the configs that fix all three. Tested on Vite 8.3.5.

by Nilay#vite#javascript#typescriptFIXED

The error, verbatim

Jump to the fix ↓
[vite] Internal server error: Failed to resolve import "@/utils/hello"
from "src/main.js". Does the file exist?

Error: [vite]: Rolldown failed to resolve import "@/utils/hello" from ".../src/main.js"
✓ Reproduced on Windows 11 Pro; Ubuntu on GitHub Actions

Tested on

Vite
8.3.5 and 8.3.0 (Rolldown); Vitest 5.0.5
Plugin
vite-tsconfig-paths 6.1.1
Node
24.14.0
OS
Windows 11 Pro; Ubuntu on GitHub Actions

✓ Last re-run on GitHub Actions · see the run · all repros

Contents
  1. The fix01
  2. Why the build passes but the dev server fails02
  3. What didn’t work03
  4. It works on Windows but fails on Linux CI: letter case04
  5. Same error, different wording05
  6. How this was tested06

@/ isn’t a real path. It’s an alias: a shortcut that something in your toolchain has to translate into src/. When nothing does, Vite stops with this in the terminal, and as a red overlay in the browser:

[vite] Internal server error: Failed to resolve import "@/utils/hello" from "src/main.js". Does the file exist?
  Plugin: vite:import-analysis

The confusing part is that the same project can build fine and still fail in the dev server. I tested every common setup on Vite 8.3.5, with vite build, the dev server (vite) and Vitest, on Windows and on Linux:

Setup vite build dev server Vitest
Only paths in tsconfig.json, no alias ✅ built if the tsconfig covers the file (allowJs for .js), ❌ otherwise ❌ HTTP 500, the error above ❌ Cannot find package '@/utils/hello'
resolve.alias with fileURLToPath ✅ ✅ ✅
resolve.alias set to "/src" ✅ ✅ ✅
resolve.tsconfigPaths: true ✅ ✅ ✅
vite-tsconfig-paths plugin ✅ ✅ ✅
resolve.alias set to "./src" (relative string) ❌ os error 2

The fix

Define the alias in vite.config.js (or .ts), so the build, the dev server and Vitest all use it:

import { defineConfig } from "vite";
import { fileURLToPath } from "node:url";

export default defineConfig({
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
});

If you already keep paths in tsconfig.json, you can tell Vite to read them instead of repeating the alias:

export default defineConfig({
  resolve: { tsconfigPaths: true },
});

Keep the matching paths entry in tsconfig.json either way. That’s what your editor and the TypeScript type checker read; Vite’s alias doesn’t reach them.

Why the build passes but the dev server fails

With only "paths": { "@/*": ["src/*"] } in tsconfig.json (plus "allowJs": true, since my files are .js) and no alias, vite build on 8.3.5 finished (✓ 5 modules transformed, ✓ built), and the bundle contained the imported code. The same result on 8.3.0. Without allowJs, or without tsconfig.json, the same build failed with Rolldown failed to resolve import "@/utils/hello". So Vite 8’s production build picks up tsconfig paths for the files that tsconfig covers, which is why one project’s build passes and another’s, with the same paths, doesn’t.

The dev server didn’t. It printed a warning at startup:

(!) Failed to run dependency scan. Skipping dependency pre-bundling. Error: The following dependencies are imported but could not be resolved:

  @/utils/hello (imported by .../src/main.js)

Are they installed?

and then answered the browser’s request for src/main.js with HTTP 500 and the “Failed to resolve import” error. Vitest, which runs through Vite’s dev pipeline, failed the same way. The fix above makes all three agree. Don’t rely on a green build: CI that only runs vite build won’t catch it.

What didn’t work

The array form also works, if you need several aliases or a regex:

import path from "node:path";
export default { resolve: { alias: [{ find: "@", replacement: path.resolve(import.meta.dirname, "src") }] } };

It works on Windows but fails on Linux CI: letter case

A file named Hello.js, imported as @/utils/hello, built without complaint on Windows. On Linux the same build failed:

[UNLOADABLE_DEPENDENCY] Could not load src/utils/hello

Windows file names ignore case and Linux’s don’t, so the alias resolves to a file Linux says doesn’t exist. Make the import match the file name exactly. If your CI runs on Linux and you develop on Windows or macOS, this is the first thing to check when an alias “suddenly” breaks only in CI.

Same error, different wording

  • Dev server: Failed to resolve import "…" from "…". Does the file exist?, in the terminal and as a red overlay in the browser.
  • Build in Vite 8: Rolldown failed to resolve import. Vite 8 bundles with Rolldown, so older answers that say “Rollup failed to resolve import” are describing the same problem.
  • A typo in a relative path (./utils/helo) gives a different build message: [UNRESOLVED_IMPORT] Could not resolve './utils/helo'. Check the spelling and extension.

How this was tested

October 31, 2026. A small project (src/main.js importing @/utils/hello, an index.html, a Vitest test importing from @/, and a tsconfig.json with "paths": { "@/*": ["src/*"] }) on Vite 8.3.5 and 8.3.0, Vitest 5.0.5 and Node 24.14.0, on Windows 11 Pro and on GitHub Actions ubuntu-latest and windows-latest. Each config was built with vite build; the dev server was started and src/main.js requested over HTTP; Vitest ran the same test. The original version of this post (September 22) tested Vite 8.3.0 with a .js project and no allowJs, which is why tsconfig paths alone failed its build. It also called resolve.tsconfigPaths “not a Vite option”; that was wrong, and it’s corrected above. The scripts and the full output are public.

— N.K., end of entry No.009

Useful? Pass it on:Post on XFollow @EmotionalMatter

Related entries

  1. No.016

    Fix: 'vite' is not recognized as an internal or external command

    Windows says the command doesn't exist, but it's right there in node_modules. Five causes, how to tell them apart in one command, and what each one needs.

    > 'vite' is not recognized as an internal or external command,

    FIXED5 minWindows
  2. No.042

    SvelteKit 3 migration, tested: what sv migrate misses

    sv migrate sveltekit-3 upgraded every package and moved the config correctly, but left three removed options in place, so the first build failed three times in a row. Worse, it rewrote a working {base}/about link into one that 404s, and that passed both the build and svelte-check.

    TESTED5 min
  3. No.080

    Fix: No 'Access-Control-Allow-Origin' header is present

    Blocked by CORS policy, but Postman and curl work fine. Five versions of the error reproduced in Chrome with Express and Vite, and which fix fits each.

    > Access to fetch at 'http://localhost:3001/plain/data' from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

    FIXED3 min
  4. No.077

    ReferenceError: process is not defined in Vite (dev vs build)

    In Vite 8, process.env.X crashes in dev but is silently undefined in the build; NODE_ENV works in both. Tested in Chromium: import.meta.env, define, polyfills.

    > Uncaught ReferenceError: process is not defined

    FIXED3 min

Post card · Every Sunday

What broke this week.

One email on Sundays: the Node, TypeScript, Python and AI SDK releases that broke builds that week, each one reproduced, with a link to the fix that worked. Never shared, and you can leave any time.

— Nilay

or follow by RSSor on X

By subscribing you agree to the privacy note. One click to leave.

tip: paste the exact error text