ReferenceError: Cannot access 'X' before initialization は、let・const・class で宣言した名前を、宣言の行が実行される前に使ったときのエラーです。 宣言より前の、使えない区間を「一時的デッドゾーン(TDZ)」と呼びます。

原因は大きく2つです。

原因 よくある形 直し方
同じファイルの中で、宣言より前に使った console.log(x); let x = 1;、関数を宣言前に呼んだ 宣言を使う場所より前に移す
ファイル同士が互いに import している(循環 import) index.js でまとめた export、ビルド後に 'd' のような1文字の名前で出る 循環を切る(直接 import する・使うのを関数の中にする)

確認環境:Node.js 22.22.2、Chromium 141、Vite 6.4.4(確認日 2026年10月11日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Cannot access before initializationの直し方」

メッセージはブラウザで違う

実行環境 メッセージ
Chrome・Edge(Chromium)、Node.js Cannot access 'X' before initialization(確認環境で同じ文言)
Firefox can't access lexical declaration 'X' before initialization(MDN による)
Safari Cannot access uninitialized variable.(MDN による)

Safari では、変数の名前が表示されません。 スタックトレースの行番号から場所を探します。

同じファイルの中で起きる例

確認環境(Node.js)で、次の形を試しました。

コード 結果
console.log(x); let x = 1; ReferenceError
console.log(y); const y = 1; ReferenceError
new Foo(); class Foo {} ReferenceError(class も同じ)
g(); const g = () => 1; ReferenceError(アロー関数を const に入れた)
console.log(typeof q); let q = 1; ReferenceError(typeof でもエラー)
let a = 1; { console.log(a); let a = 2; } ReferenceError(ブロックの中の a が外の a を隠す)
console.log(z); var z = 1; undefined(var はエラーにならない)
h(); function h() {} 動く(function 宣言は前から呼べる)
console.log(nope);(宣言なし) nope is not defined(別のエラー)

function h() {} の形は宣言より前に呼べますが、const g = () => {} の形は呼べません。 関数を const に書き換えたら、このエラーが出るようになった、というのはこのためです。

is not defined は、その名前がどこにも宣言されていないエラーです。 before initialization は「宣言はあるが、まだその行が実行されていない」なので、見るところが違います。

「書いた位置」ではなく「実行した順番」

コード 結果
function f() { return c; } f(); let c = 1; ReferenceError(f() を呼んだ時点で、c の行はまだ)
function f() { return c; } let c = 1; f(); 動く(1)

関数の中で使うだけなら、宣言より前に書いてあっても問題ありません。呼んだ時点で、宣言の行が実行済みかどうかで決まります(MDN の let の解説も同じ)。React のコンポーネントや、addEventListener に渡す関数で、あとから呼ばれる場合は動くのに、初期化の途中で呼ぶとエラーになるのはこのためです。

直し方:宣言を、使う場所より前に移します。 var に戻すとエラーは消えますが、undefined のまま進んでしまうので、別の不具合になります。

ファイル同士の循環 import で起きる例

// a.mjs
import { b } from './b.mjs';
export const A = 'A';
// b.mjs
import { A } from './a.mjs';
export const b = 'b';
console.log('b sees', A);    // ファイルの一番上の階層で、A を使う
(a.mjs を読み込む)
file:///.../b.mjs:3
console.log('b sees', A);
                      ^
ReferenceError: Cannot access 'A' before initialization

a.mjs が読み込みを始める → 1行目で b.mjs へ → b.mjs が A を使う → a.mjs はまだ1行目の途中なので、A が初期化されていない、という順番です。

よくある形:index.js でまとめた export

// components/index.js(まとめて export する)
export { Card } from './Card.js';
export { Button } from './Button.js';
// components/Card.js
import { Button } from './index.js';          // ← 自分を export している index.js から import
export const CardButton = Button('OK');        // ← 読み込み時に Button を使う
export const Card = () => `Card ${CardButton}`;
ReferenceError: Cannot access 'Button' before initialization

index.js が Card を先に読み込み、Card.js は index.js から Button を取ろうとするが、index.js はまだ Button の行まで進んでいないため、エラーになりました。index.js の export の順番を入れ替えると動いたので、ファイルを1つ足しただけで突然出る、ということが起きます。

Vite・ビルド後は「'd'」のような1文字の名前になる

同じコードを Vite でビルドし、Chromium で開くと、名前が1文字になりました。

実行のしかた エラー
Vite の開発サーバー Cannot access 'Button' before initialization(Card.js:2:27)
vite build(圧縮あり) Cannot access 'd' before initialization
vite build --minify false 圧縮しない。出力で Button を探すと、使う行が定義より前にあると分かる
// vite build --minify false の出力(抜粋)
const CardButton = Button("OK");      // 使う
const Card = () => `Card ${CardButton}`;
const Button = (label) => `[${label}]`;  // 定義はあと

ビルドすると全部のファイルが1つにまとまり、循環 import が「同じファイルの中で、宣言より前に使った」形に変わります。 本番だけで出る 'd' のようなエラーは、--minify false でビルドし直すか、ソースマップで元の名前を確かめます。

循環 import の直し方

直し方 書き方
index.js を経由せず、直接 import import { Button } from './Button.js'(同じフォルダーの中では index.js を使わない)
使うのを関数の中にする export const Card = () => \Card ${Button('OK')}`;`(呼ばれたときに使う)
共通の部分を別のファイルに分ける 互いに使う部分を、どちらも import しないファイルに移す

確認環境では、Card.js の import を ./Button.js に変える、またはButton を使うのを関数の中に移すと、どちらも Card [OK] と動きました。

読み込みの時点(ファイルの一番上の階層)で、import した値を使っていないかを見ます。関数の中で使うだけなら、呼ばれるころには読み込みが終わっています。

確認の手順(チェックリスト)

順番 確認すること 方法
1 エラーの名前と行 Cannot access 'X' の X と、スタックトレースの1行目
2 同じファイルに X の宣言があるか あれば、宣言を使う場所より前に移す
3 X が import したものか 循環 import を疑う
4 index.js 経由の import か 同じフォルダーの中では直接 import に
5 読み込み時に使っていないか ファイルの一番上の階層での呼び出しを、関数の中へ
6 1文字の名前(ビルド後) vite build --minify false かソースマップで元の名前を確かめる

よくある質問

Q. let を var に変えれば直りますか。 A. エラーは出なくなりますが、値は undefined のまま使われ、別の場所で Cannot read properties of undefined などになります。宣言の順番か、循環 import を直します。

Q. 循環 import を見つける方法はありますか。 A. スタックトレースに出ているファイルの import をたどると、輪になっている箇所が分かります。プロジェクト全体では、ESLint の eslint-plugin-import の import/no-cycle ルールが使えます。上の Card.js と index.js の例では、確認環境(ESLint 9.39)で Dependency cycle detected import/no-cycle と、両方のファイルが検出されました。

まとめ

  • let・const・class を、宣言の行が実行される前に使ったエラー
  • var は undefined、function 宣言は前から呼べるので、書き換えたときに出やすい
  • 書いた位置ではなく、実行した順番で決まる
  • 循環 import でも出る。index.js でまとめた export の順番で、突然出ることがある
  • ビルド後は 'd' のような1文字の名前になる。--minify false で元の名前を確かめる
  • 直し方は宣言を前へ・直接 import・使うのを関数の中へ

参考・出典

確認日はいずれも 2026年10月11日です。

  • MDN「ReferenceError: can't access lexical declaration 'X' before initialization」:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_access_lexical_declaration_before_init
  • MDN「let」(Temporal dead zone (TDZ)):https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Cannot access before initializationの直し方」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?