The Scenario
You just added a profile_picture_url column to your User entity. You hit 'Run,' expecting to see your new feature in action, but the app crashes instantly. Instead of your UI, you see a java.lang.IllegalStateException in the Logcat. This happens because the structure of your database in the code no longer matches the file saved on the device.
java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number.
Why This Error Happens
Room acts as a gatekeeper for your local data. Every time you compile your project, Room generates a unique identity hash. Think of this as a 32-character fingerprint of your entire schema—including tables, columns, and constraints. Room stores this hash in a hidden table called room_master_table inside your SQLite file.
When the app launches, Room compares the fingerprint of your current code against the one stored on the disk. If they differ and the version number remains the same, Room panics. It stops the app to prevent data corruption, as it doesn't know how to move your old data into the new structure.
Quick Fix (During Prototyping)
If you are still in the early stages of development and don't have real user data to worry about, you can fix this in seconds by wiping the slate clean.
- Clear App Data: Open Settings > Apps > [Your App] > Storage and tap "Clear Data."
- Reinstall: Delete the app from your emulator or phone and run it again from Android Studio.
This deletes the .db file entirely. On the next launch, Room creates a fresh database using your new schema and saves the updated hash. This is fine for dev, but never do this in production.
Permanent Fixes for Production
When your app is live and has thousands of users, you can't ask them to delete their data. You have two reliable ways to handle the change.
Option 1: Destructive Migration (Data Loss)
Use this if your database is just a cache and losing the data won't hurt the user experience. First, bump the version number from 1 to 2 (or whatever your current version is).
@Database(entities = [User::class], version = 2)
abstract class AppDatabase : RoomDatabase() { ... }
Next, tell the Room builder to use fallbackToDestructiveMigration():
val db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "app-db"
)
.fallbackToDestructiveMigration()
.build()
Room will detect the version jump, delete the old tables, and recreate them. No crash, but the database starts empty.
Option 2: Manual Migration (Data Preservation)
This is the professional standard. You must write a script that tells SQLite exactly how to modify the existing tables. Suppose you added an age column to a User table.
Step 1: Increase the version in your @Database class.
@Database(entities = [User::class], version = 2)
abstract class AppDatabase : RoomDatabase() { ... }
Step 2: Create a migration object. This code runs a specific SQL command to update the table structure without touching the existing rows.
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE User ADD COLUMN age INTEGER NOT NULL DEFAULT 0")
}
}
Step 3: Register the migration in your builder.
Room.databaseBuilder(context, AppDatabase::class.java, "app-db")
.addMigrations(MIGRATION_1_2)
.build()
Verification and Debugging
How do you know the migration actually worked? Use the Database Inspector in Android Studio (View > Tool Windows > App Inspection). You can see the live table structure and run SQL queries to verify your new columns exist.
If you're dealing with a complex schema and can't figure out why the hashes don't match, check your exported schema files. Set exportSchema = true in your database annotation. This generates a JSON file for every version. Sometimes, even a small detail like a DEFAULT value difference can cause a mismatch. I often use the Hash Generator at ToolCraft to compare the MD5 hashes of these JSON files. It helps identify if a hidden character or a formatting change is causing Room to reject the schema.
Pro tip: Always update your version number the moment you change an @Entity. It saves you from hunting down mysterious crashes later in the day.

