Skip to content
Latchkey

Vitest "Cannot access before initialization" - vi.mock Fix

Vitest hoists vi.mock calls to the top of the file, above your imports and variable declarations. A mock factory that references an outer variable then runs before that variable exists, throwing a reference error.

What this error means

A test using vi.mock('./api', () => ({ ... })) fails with "Cannot access 'mockFn' before initialization" or a Vitest message about resolving the mock. The variable looks defined above - but hoisting moved the mock call above it.

Vitest output
ReferenceError: Cannot access 'mockedFetch' before initialization

 ❯ src/user.test.ts:3:18
   1| import { vi } from 'vitest';
   2| const mockedFetch = vi.fn();
   3| vi.mock('./api', () => ({ fetchUser: mockedFetch }));

Diagnose it: flake, environment, or genuine failure?

Before debugging the assertion, establish whether the test is deterministic. A test that fails only in CI is usually order-dependent, time-dependent, or racing something, and fixing the assertion will not help.

Terminal
# does it fail in isolation?
npx vitest run path/to/file.test.ts

# is it order dependent? run the suite in a random order twice
npx vitest run --sequence.shuffle

# is it a race? run the same file repeatedly
for i in $(seq 1 20); do npx vitest run path/to/file.test.ts || break; done

Common causes

Mock factory references a non-hoisted variable

vi.mock is hoisted above imports and const/let declarations. A factory that closes over a normal variable runs before that variable is initialized, so it throws.

Relying on import order that hoisting changes

Because the mock is hoisted, the real module may already be imported elsewhere before the mock is registered, leaving the mock partially applied.

How to fix it

Declare mocks with vi.hoisted

vi.hoisted runs before the hoisted vi.mock, so variables it returns are safe to reference inside the factory.

user.test.ts
import { vi } from 'vitest';
const { mockedFetch } = vi.hoisted(() => ({ mockedFetch: vi.fn() }));
vi.mock('./api', () => ({ fetchUser: mockedFetch }));

Keep the factory self-contained

  1. Define vi.fn() instances inside the factory, or via vi.hoisted, not as outer consts.
  2. Use await import(...) after vi.mock if you need the mocked module instance.
  3. Avoid referencing test-scope variables the hoist would skip past.

CI-only causes worth ruling out

  • Runners have fewer cores than a laptop, so timing-sensitive tests that pass locally fail under contention.
  • No TTY and a different locale or timezone. Snapshot tests containing formatted dates or numbers are the usual casualty; pin TZ and LANG in the job.
  • Parallel workers sharing a database, a port, or a temp directory. Give each worker its own namespace.
  • Default timeouts calibrated on a fast machine. A cold runner is slower on first execution, especially before any cache warms.

How to prevent it

  • Use vi.hoisted for any value a mock factory needs to reference.
  • Keep mock factories free of outer-scope variable references.
  • Remember vi.mock runs first regardless of where you wrote it.

Frequently asked questions

What causes Vitest "Cannot access before initialization"?
There are 2 common causes: mock factory references a non-hoisted variable and relying on import order that hoisting changes. vi.mock is hoisted above imports and const/let declarations.
How do I fix Vitest "Cannot access before initialization"?
There are 2 fixes depending on which cause you have: declare mocks with vi.hoisted and keep the factory self-contained. Work through them in order, since the first is the most common.
What does Vitest "Cannot access before initialization" actually mean?
A test using vi.mock('./api', () => ({ ...
How do I stop Vitest "Cannot access before initialization" happening again?
Use vi.hoisted for any value a mock factory needs to reference. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card