Skip to content
Latchkey

Node.js Require Cycle - Fix "Cannot access before initialization"

When module A imports B and B imports A, one of them runs before the other has finished initializing. Under ESM that surfaces as a temporal-dead-zone error; under CJS it surfaces as an unexpectedly undefined import.

What this error means

A build or test crashes at module load with ReferenceError: Cannot access 'X' before initialization (ESM), or a value imported from a sibling module is mysteriously undefined (CJS). It is order-dependent, so it can appear only in CI where the entry path differs.

Node output
ReferenceError: Cannot access 'helper' before initialization
    at file:///app/src/a.js:2:14
    at ModuleJob.run (node:internal/modules/esm/module_job)
# (ESM live-binding evaluated before the cyclic module finished)

Common causes

Two modules import each other at top level

A ↔ B import cycle means whichever loads first sees the other half-initialized. ESM throws a TDZ error; CJS yields a partially-populated module object (often undefined).

A barrel/index file re-exports into a cycle

An index.js that re-exports everything can route an import back through itself, creating a cycle that only bites under a particular load order.

How to fix it

Break the cycle

Extract the shared piece into a third module both sides import, removing the A↔B edge.

JavaScript
// before: a.js <-> b.js cycle
// after: move the shared symbol to shared.js
// a.js  -> import { helper } from './shared.js'
// b.js  -> import { helper } from './shared.js'

Locate and confirm the cycle

  1. Run with node --trace-warnings or a cycle detector (e.g. madge) to find the loop.
  2. Avoid using a cyclic import’s value at module top level - defer it inside a function if you cannot restructure yet.
  3. Watch barrel/index re-exports, a common hidden source of cycles.

How to prevent it

  • Keep dependency direction acyclic; push shared code into leaf modules.
  • Run a cycle detector in CI for large module graphs.
  • Be cautious with barrel files that re-export widely.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →