RailsでActiveRecord::NoEnvironmentInSchemaErrorが出たときの原因と対応方法の備忘録。Railsのバージョンは5.1.5です。

開発環境でDBを作り直す際、db:setupではなくdb:migrateを使ってマイグレーションをする場合、以下のコマンドでDBのセットアップを行っていました。

このとき、db:migrateでコケて、マイグレーションファイルを修正後に再度上記のコマンドを叩くと以下のように、ActiveRecord::NoEnvironmentInSchemaErrorが発生します。

対応方法としてはエラーに記載の通り、 db:environment:setを実行すれば良いのですが、なぜこのエラーが発生し、db:environment:setのRakeタスクで解消するのか?を説明していきます。

ActiveRecord::NoEnvironmentInSchemaError

まずはNoEnvironmentInSchemaErrorがどういったケースで発生するのかをコードベースで追っていきます。

dropのタスクではcheck_protected_environmentsが呼ばれます。このタスクはActiveRecord::Tasks::DatabaseTasks.check_protected_environments!を呼び出します。

ActiveRecord::Migrator.last_stored_environmentでActiveRecord::InternalMetadata[:environment]を取得できなければNoEnvironmentInSchemaErrorが発生します。

ActiveRecord::InternalMetadataはar_internal_metadataテーブルからRAILS_ENVの値に応じたレコードを取得・生成します。よって、NoEnvironmentInSchemaErrorが発生する=ar_internal_metadataのレコードが存在しない、ということになります。

一方、db:migrateのときはActiveRecord::Migrator#record_environment経由でActiveRecord::InternalMetadata[:environment]が設定されます。

マイグレーションが終わったタイミングでar_internal_metadataのレコードが生成されるので、マイグレーションがエラーになるとレコードが生成されません。したがって、この状態でdb:dropを叩くとar_internal_metadataのレコードが見つからずNoEnvironmentInSchemaErrorが発生することになります。

ということで、解決方法もar_internal_metadataを生成すれば良いので、db:environment:setを叩けば良いことになります。