【完全版】Ruby/Rails「NameError: uninitialized constant」エラーの原因と対処法|Zeitwerk時代の定数解決

  • 作成日 2026.06.26
  • rails
【完全版】Ruby/Rails「NameError: uninitialized constant」エラーの原因と対処法|Zeitwerk時代の定数解決

Ruby on Rails 開発で頻発するエラー:

NameError (uninitialized constant User)
NameError (uninitialized constant Api::V1::UsersController)
Zeitwerk::NameError (expected file app/models/vat.rb to define constant Vat, but didn't)
NameError: uninitialized constant in an initializer

NameError: uninitialized constant は「Rubyが指定された定数を見つけられない」エラー。Rails 6から標準採用された Zeitwerk autoloader の規約に従っていない場合、シビアに発生します。

  • ファイル名・クラス名の対応が分からない
  • ネストしたモジュールで急に発生
  • VAT、CSV、JSON 等の頭字語クラスで頻発
  • 本番でだけ発生する(development では動く)
  • gem内のクラスを参照したら出る
  • Rails アップグレード後に大量発生
  • require で対処すべきか、autoload に任せるべきか

など、初学者から中級者まで悩まされ続けます。

本記事では、NameError: uninitialized constantすべての原因と対処法を、Zeitwerk 時代の実践的なリファレンスとして整理します。エラーの本質、Zeitwerk の規約、ファイル命名規則、acronym対応、autoload_paths、zeitwerk:check、lib/ の扱い、Classic Autoloader からの移行、Gem との競合、関連エラー、FAQまで完全網羅。この1本で uninitialized constant エラーを完全攻略できます。


目次

結論:今すぐ試すべき3ステップ

時間がない方向けに、まず実施すべき手順を示します。

ステップ1:エラーから定数名を取得

NameError (uninitialized constant Api::V1::UsersController)
                                  ↑
                          ロードできなかった定数

ステップ2:ファイル配置を確認

Api::V1::UsersController なら:

app/controllers/api/v1/users_controller.rb
                ↑          ↑
              api/        v1/

ディレクトリ名と名前空間(モジュール)が完全一致している必要があります。

ステップ3:規約に従って修正

# app/controllers/api/v1/users_controller.rb
module Api
  module V1
    class UsersController < ApplicationController
      # ...
    end
  end
end

それでも解決しない場合:

# Zeitwerk チェック
bin/rails zeitwerk:check

詳細は以下で解説します。


まず押さえる:定数とは何か

Ruby の定数

Ruby では大文字で始まる識別子はすべて定数。クラス・モジュールも定数の一種:

PI = 3.14         # 定数
class User end    # User も定数(Class オブジェクトを格納)
module Api end    # Api も定数(Module オブジェクト)

NameError: uninitialized constant とは

そのスコープから参照可能な場所に、その名前の定数が定義されていない」エラー:

puts User
# => NameError (uninitialized constant User)

Ruby の定数探索ルール

Rubyは以下の順序で定数を探します:

  1. 現在のクラス・モジュール
  2. そのスーパークラス
  3. トップレベル(Object)
class Foo
  BAR = 100
  
  def value
    BAR   # 1. Fooクラス内で見つかる
  end
end

ネストしたモジュールでは:

module Outer
  module Inner
    CONST = 1
  end
end

# 1. Outer::Inner にアクセス → 内側から参照
module Outer
  module Inner
    puts CONST  # 1(同じスコープ)
  end
end

# 2. 外から参照
puts Outer::Inner::CONST  # 1(完全修飾)

# 3. 失敗例
puts Inner::CONST
# => NameError (uninitialized constant Inner)

Zeitwerk: Rails 7+ のautoloader

Zeitwerk とは

Rails 6で導入され、Rails 7以降でデフォルトの新しい自動ロード機構。これにより require を書かなくても、定数参照時にファイルが自動的にロードされます。

# どこかでこれを書くだけで自動でロード
user = User.new   # app/models/user.rb が自動ロード

Zeitwerk の基本規約

ファイル名 = スネークケース、クラス名 = キャメルケース:

ファイルパス期待される定数
app/models/user.rbUser
app/models/users_helper.rbUsersHelper
app/controllers/users_controller.rbUsersController
app/controllers/api/v1/users_controller.rbApi::V1::UsersController
app/services/payment_processor.rbPaymentProcessor

ディレクトリ階層 = モジュール階層:

app/models/
├── user.rb              # User
├── api/
│   ├── client.rb        # Api::Client
│   └── v1/
│       └── request.rb   # Api::V1::Request
# app/models/api/v1/request.rb
module Api
  module V1
    class Request
      # ...
    end
  end
end

autoload_paths

Zeitwerk が自動ロード対象とするディレクトリ:

# 標準の autoload_paths(Rails 7+)
- app/assets
- app/controllers
- app/controllers/concerns
- app/helpers
- app/jobs
- app/mailers
- app/models
- app/models/concerns
- app/views

確認:

Rails.application.config.autoload_paths
# または
Rails.autoloaders.main.dirs

eager_load_paths

本番環境(production)では、起動時にすべてのファイルがロードされます:

# config/environments/production.rb
config.eager_load = true

これにより:

  • 起動時に一括チェック(タイポ等が早期発見)
  • リクエスト処理時のロード遅延なし
  • メモリ使用量は増える

発生原因と対処パターン

原因1:ファイル名とクラス名の不一致

# app/models/user.rb
class Users   # ← ❌ ファイル名 user.rb なら User
end

実行:

User
# => NameError (uninitialized constant User)

対処: ファイル名と完全一致させる:

# app/models/user.rb
class User    # ✅
end

または:

# app/models/users.rb(ファイル名を変える)
class Users
end

原因2:名前空間(モジュール)が不一致

# app/models/api/v1/user.rb
class User   # ❌ Api::V1::User と期待される
end

エラー:

expected file app/models/api/v1/user.rb to define constant Api::V1::User, but didn't

対処:

# app/models/api/v1/user.rb
module Api
  module V1
    class User
      # ...
    end
  end
end

原因3:頭字語(acronym)の問題

「VAT」「CSV」「JSON」「HTML」「API」「URL」「PDF」のような頭字語は注意。

# app/models/csv_importer.rb
class CSVImporter   # ❌ Zeitwerk は CsvImporter を期待
end

エラー:

expected file app/models/csv_importer.rb to define constant CsvImporter, but didn't

対処1: acronym設定

# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym "CSV"
  inflect.acronym "JSON"
  inflect.acronym "HTML"
  inflect.acronym "API"
  inflect.acronym "URL"
  inflect.acronym "PDF"
  inflect.acronym "VAT"
end

設定後、csv_importer.rbCSVImporter として解決されます。

対処2: ファイル名を変える

app/models/csv_importer.rb     # ❌ CSVImporter を期待するなら acronym 設定必要
↓
app/models/csv_importer.rb     # ✅ Zeitwerk default なら CsvImporter で OK

原因4:複数定数の同一ファイル定義

Zeitwerk は 1ファイル1定数が原則:

# app/models/admin.rb
module Admin
  class User       # ❌ Admin::User を別ファイルで期待
  end
  
  class Article
  end
end

対処: 名前空間モジュールとクラスを別ファイルに:

# app/models/admin.rb
module Admin
end
# app/models/admin/user.rb
module Admin
  class User
  end
end
# app/models/admin/article.rb
module Admin
  class Article
  end
end

原因5:autoload_paths 外のディレクトリ

lib/ ディレクトリは標準では autoload 対象外:

# lib/services/payment.rb
class Payment
end

# 別の場所で
Payment.new
# => NameError

対処1: autoload_paths に追加

# config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
config.eager_load_paths += %W(#{config.root}/lib)

対処2: app/ 配下に移動

mv lib/services app/services

Rails 7+ では lib/ を自動的に autoload しない設計。app/ 内のサービスやリブラリは app/services 等に置くのが推奨。

原因6:require_dependency の名残

Rails 6以前で require_dependency を使っていた箇所:

require_dependency 'some_file'    # 非推奨(Zeitwerkでは不要)

対処: 削除する。Zeitwerk が自動的に解決します。

原因7:spring の影響(古いRails)

Rails 7.1+ では Spring が廃止されたため、現在この問題はほぼなし。古いRailsで:

bin/spring stop

でクラスキャッシュを破棄。

原因8:Gem の autoload 競合

Gemのクラスを直接参照しようとして失敗:

Stripe::Customer.create(...)
# => NameError (uninitialized constant Stripe)

対処: Gemfile に追加し、bundle install:

# Gemfile
gem 'stripe'
bundle install

require が必要な gem の場合:

# Gemfile
gem 'stripe', require: 'stripe'

または初期化ファイルで:

# config/initializers/stripe.rb
require 'stripe'

原因9:initializer のロード順

# config/initializers/api_client.rb
ApiClient.configure do |c|
  # ApiClient が未定義の場合エラー
end

initializers は eager_load の前に実行されるため、Zeitwerk が未ロードのクラスを参照するとエラー。

対処1: 明示的に require(gem の場合)

require 'api_client'
ApiClient.configure do |c|
  # ...
end

対処2: to_prepare で遅延実行

# config/initializers/setup.rb
Rails.application.config.to_prepare do
  ApiClient.configure do |c|
    # ...
  end
end

原因10:concern と config

# app/models/concerns/searchable.rb
module Searchable
end
# app/models/article.rb
class Article < ApplicationRecord
  include Searchable
end

app/models/concerns/ は autoload_paths に含まれるが、名前空間は無視されます。つまり Searchable をそのまま参照できる。

注意: Models::Searchable のような名前空間にしたい場合は、app/models/searchable.rb に置くか、ディレクトリ構造を整える必要があります。


zeitwerk:check の活用

Rails のコマンドで autoload 不整合をチェック。

実行

bin/rails zeitwerk:check

正常時

Hold on, I am eager loading the application.
All is good!

エラー時

Hold on, I am eager loading the application.
expected file app/models/api/v1/user.rb to define constant Api::V1::User, but didn't

該当ファイルの定数定義を修正。

CI/CD で実行

# .github/workflows/ci.yml
- name: Check autoloader
  run: bin/rails zeitwerk:check

本番デプロイ前に autoload 問題を防げます。

autoload のログ確認

# config/application.rb
config.after_initialize do
  Rails.autoloaders.log!  # autoload動作をログ出力
end

または:

# 一時的に有効化
RAILS_AUTOLOADERS_LOG=1 bin/rails server

これで「どの定数がどのファイルからロードされたか」が追跡できます。


Classic Autoloader からの移行

Rails 6以前の Classic Autoloader を使っているプロジェクトの移行。

現在のモード確認

Rails.autoloaders.zeitwerk_enabled?
# => true なら Zeitwerk

または:

Rails.application.config.autoloader
# => :zeitwerk または :classic

Zeitwerk 化の手順

ステップ1:load_defaults を設定

# config/application.rb
config.load_defaults 7.0   # または 8.0

これで Zeitwerk がデフォルトに。

ステップ2:zeitwerk:check 実行

bin/rails zeitwerk:check

エラーが出るファイルを一つずつ修正。

ステップ3:acronym 設定

頭字語を使うクラスがあれば:

# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym "API"
  inflect.acronym "CSV"
  # ...
end

ステップ4:複数定数の分割

1ファイル複数定数の構造を、Zeitwerk規約に合わせて分割。

ステップ5:require_dependency の削除

# 削除
require_dependency 'some_file'

ステップ6:lib/ の扱い見直し

# 必要に応じて autoload_paths に追加
config.autoload_paths += %W(#{config.root}/lib)
config.eager_load_paths += %W(#{config.root}/lib)

または app/ に移動。


開発環境 vs 本番環境

開発環境(development)

eager_load = false のため、必要時のみロード:

  • メモリ効率良い
  • 起動が高速
  • 一部のファイル問題は気付かない可能性

本番環境(production)

eager_load = true起動時に全ロード:

  • すべてのファイルが読まれる
  • タイポ等は起動時に発覚
  • メモリ使用量は増える

ローカルで本番モード再現

開発中にも production 相当の挙動を試したい場合:

# config/environments/development.rb
config.eager_load = true   # 一時的に true に

または:

RAILS_ENV=production bin/rails server

test 環境

config.eager_load = ENV["CI"].present? のような条件設定が一般的:

# config/environments/test.rb
config.eager_load = ENV["CI"].present?

CIでは production 同等の挙動でテスト、ローカルテストでは高速化。


Gem 開発時の Zeitwerk 統合

Rails gem を開発する場合の autoload セットアップ。

gem 内での Zeitwerk 利用

# lib/my_gem.rb
require "zeitwerk"

module MyGem
  loader = Zeitwerk::Loader.for_gem
  loader.setup
end

Zeitwerk::Loader.for_gem が自動的に lib/my_gem/ 配下を autoload 対象に。

ファイル命名

lib/
├── my_gem.rb
└── my_gem/
    ├── client.rb     # MyGem::Client
    ├── version.rb    # MyGem::VERSION
    └── api/
        └── v1.rb     # MyGem::Api::V1

Engine gem

Rails Engine の場合:

# lib/my_engine/engine.rb
module MyEngine
  class Engine < ::Rails::Engine
    isolate_namespace MyEngine
  end
end

isolate_namespace で namespace 分離。app/ 配下のファイルは自動 autoload。


ActiveSupport::Autoload との違い

Rails 内部や一部 gem で使われる古い手法:

module Api
  extend ActiveSupport::Autoload
  
  autoload :Client
  autoload :Request
end

これは明示的に autoload を登録する方式。Zeitwerk と混在すると問題が起きるため、新規 gem では Zeitwerk 推奨。


トラブルシューティング

Zeitwerk::NameError vs NameError

エラーは2種類あります:

エラー意味
Zeitwerk::NameErrorファイル存在するが、期待する定数が定義されていない
NameError定数自体が見つからない

「expected file X to define constant Y」

Zeitwerk::NameError: expected file app/models/vat.rb to define constant Vat, but didn't

原因: ファイル名と定数名の不一致(典型的に acronym 問題)

対処: acronym 設定、またはファイル名・クラス名を一致させる

「uninitialized constant X::Y」

NameError: uninitialized constant Api::V1::User

原因: そもそも該当ファイルが存在しないか、autoload_paths 外

対処: ファイル配置確認、autoload_paths追加

initializer 内で NameError

# config/initializers/setup.rb
SomeService.configure { ... }
# => NameError

対処: to_prepare で遅延実行

Rails.application.config.to_prepare do
  SomeService.configure { ... }
end

Rails console で動くが server で動かない

console は autoload が動的に効きますが、production server では eager_load 時に問題発覚:

RAILS_ENV=production bin/rails zeitwerk:check

事前に確認。

gem の名前空間と衝突

# app/models/stripe.rb(自作モデル)
class Stripe
end

# Stripe gem も存在
require 'stripe'

対処: 自作モデルを別名にする

# app/models/stripe_account.rb
class StripeAccount
end

Spring を完全に削除(古いRails)

# Gemfile
# gem 'spring'   # 削除
bundle install

Rails 7.1+ では Spring は廃止されています。

Sprockets と Zeitwerk の競合

app/assets/ 内のアセットファイルが間違って autoload 対象になる場合:

# config/application.rb
Rails.autoloaders.main.ignore(
  Rails.root.join("app/assets")
)

よくある質問(FAQ)

Q1. Rails 7/8 でも require は必要ですか?

app/ 配下のクラスには不要(Zeitwerk が自動ロード)。 gem や Ruby 標準ライブラリには必要:

require 'json'
require 'stripe'

Gemfilerequire: 'stripe' していれば、自動的に require されます。

Q2. lib/ ディレクトリのファイルを使いたい

選択肢:

A. autoload_paths に追加

config.autoload_paths += %W(#{config.root}/lib)
config.eager_load_paths += %W(#{config.root}/lib)

B. app/ 配下に移動(推奨)

mv lib/services app/services

Q3. acronym 設定の確認方法

# console で
"vat".camelize
# => "VAT" になっていれば設定済み

"csv_importer".camelize
# => "CSVImporter"

Q4. クラスを再ロード(reload)する方法

# console で
reload!

または:

Rails.application.reloader.reload!

開発環境ではファイル変更で自動的に再ロード。

Q5. constantize で NameError

"User".constantize
# => User クラス取得

"NonExistent".constantize
# => NameError

safe_constantize を使うと nil を返す:

"NonExistent".safe_constantize
# => nil

Q6. 名前空間モジュールを別ファイルにすべき?

app/models/
├── api.rb            # module Api
└── api/
    ├── client.rb     # Api::Client
    └── v1/
        └── request.rb # Api::V1::Request

Zeitwerk 規約ではモジュール Apiapp/models/api.rb存在することが期待されます。ファイルが無い場合は自動で空モジュールが生成されますが、明示的に書く方が無難。

Q7. テンプレートで NameError

<%= @user.name %>
<% # @user が nil の場合は別エラー(NoMethodError)%>

ビューでの NameError は ローカル変数の typo の可能性:

<%# render の引数渡し漏れ %>
<%= render "user_card", user: @user %>

部分テンプレート内で:

<%# locals: (user:) -%>
<%= user.name %>

Q8. テストで NameError

# spec/models/user_spec.rb
require "rails_helper"

RSpec.describe User do
  # ...
end

rails_helperRails.application を ロードしていないと autoload が効きません。require "rails_helper" を必ず先頭に。

Q9. 環境別の挙動

# development: 必要時にロード(lazy)
# test: 設定次第
# production: 起動時に全ロード(eager)

production で問題に気づくケースが多いため、CIで eager_load=true 推奨。

Q10. ActiveSupport::Autoload は使うべきか?

新規開発ではZeitwerk推奨ActiveSupport::Autoload は古いコードの互換性目的。

Q11. Engine gem との競合

Engine gem が autoload_paths を追加することで、メインアプリの定数と衝突する場合:

# 該当 gem の Engine.rb 確認
# isolate_namespace の有無を確認

isolate_namespace していない gem は名前空間衝突しやすい。

Q12. Zeitwerk のドキュメントは?

公式ドキュメント:


関連エラーとの違い

NoMethodError nil:NilClass

@user.name
# @user が nil → NoMethodError

定数自体は存在するが、メソッド呼び出し対象がnil。詳細はnil:NilClass の記事

LoadError

require 'non_existent'
# => LoadError

ファイル自体が見つからない場合。

Zeitwerk::NameError

Zeitwerk::NameError: expected file X.rb to define constant Y

ファイルはあるが、期待する定数が定義されていない。

ArgumentError: wrong number of arguments

メソッド呼び出しの引数数違い。定数とは無関係。

違いの一覧

エラー発生原因
NameError uninitialized constant定数が見つからない
Zeitwerk::NameErrorファイルあるが定数定義間違い
LoadErrorファイル自体が見つからない
NoMethodErrorメソッド呼び出し対象がnil/不適切
ArgumentErrorメソッド引数の数や型違い

参考リンク・関連資料

Ruby / Rails 公式

関連 Gem

関連記事(本サイト)


まとめ

NameError: uninitialized constant は Ruby/Rails で頻出するエラーですが、Zeitwerk の規約を理解すれば確実に対処できます。要点を再整理します。

  • 本質: Rubyがその定数を見つけられないエラー
  • Zeitwerk 規約: ファイル名(スネークケース)= 定数名(キャメルケース)
  • ディレクトリ = 名前空間: api/v1/user.rbApi::V1::User
  • acronym: VAT/CSV/JSON 等は inflect.acronym で設定
  • 1ファイル1定数: 複数定数定義は分割
  • lib/ の扱い: 標準では autoload 対象外、必要なら追加
  • zeitwerk:check: CI/CDで実行して事前検出
  • eager_load: production は起動時に全ロード
  • initializer: 早期ロード問題は to_prepare で対処
  • Classic からの移行: load_defaults 7.0+ で Zeitwerk 化

これらの知識は、Rails アプリの設計・リファクタリング・本番デプロイ・Gem 開発などあらゆる場面で活用できます。本記事をブックマークしておけば、uninitialized constant エラーに遭遇した時の対応が大幅に効率化されます。


本記事は2026年6月時点の情報をもとに、Ruby on Rails 7.x / 8.x、Zeitwerk 最新版での動作確認・公式ドキュメントに基づき作成しています。Rails のバージョンによって挙動が異なる場合があるため、最新の情報はRails公式ガイドもあわせてご確認ください。