You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Using @Upsert annotation of room2/room3 and SQLCipherDriver leads to UNIQUE constraint failed exception #95
Library version used: 4.19.1
Room version used: 2.8.5 & 3.0.3
I'm currently upgrading to room3 and during the Prepare and modernize in Room 2.x part of the Android documentation I'm running into an runtime error.
At the stage Set the SQLite driver I'm doing it like your documentation says and pass a SQLCipherDriver to the databaseBuilder.
Using the @Upsert annotation (both in room2 and room3) now leads to an UNIQUE constraint failed exception at runtime. This also happens after a complete upgrade to room3.
This does not happen when using the room2 way with SupportOpenHelperFactory.
catch the constraint exception, check it's a uniqueness violation,
fall back to UPDATE.
Step 2 is the fragile part: it is a catch on a specific exception type plus a string match on the message. Room's driver-based adapter catches androidx.sqlite.SQLiteException.
SupportOpenHelperFactory went through Room's SupportSQLiteDriver wrapper, where the error surfaced in a form Room's fallback recognised, so step 3 ran and you never saw the error. SQLCipherDriver hands Room SQLCipher's own
net.zetetic.database.sqlcipher.SQLiteConstraintException instead (the (code 1555); query: … message format is zetetic's native formatter). That type is not androidx.sqlite.SQLiteException, so the catch never fires, the UPDATE
fallback is skipped, and the raw insert failure propagates to you.
This is the same class of bug as sqlcipher-android#23 (#23) (@upsert + SQLCipher) and robolectric#8469 (robolectric/robolectric#8469) — any driver
that wraps SQLite errors in its own exception hierarchy breaks Room's upsert fallback. sqlcipher-android#84 (#84) tracks the broader Room 3 / SQLiteDriver migration gap.
Hm, after some more testing the upsert seems to be working, but the exception is still logged 🤔
Also works when the primary key is a String and not auto generated...
11:51:57.521 D [User(firstName=John, lastName=Doe, uid=1)]
11:51:57.522 E exception: UNIQUE constraint failed: User.uid (code 1555); query: INSERT INTO `User` (`first_name`,`last_name`,`uid`) VALUES (?,?,nullif(?, 0))
11:51:57.525 I Database keying operation returned:0
11:51:57.979 D [User(firstName=Jane, lastName=Doe, uid=1)]
When using the AndroidSQLiteDriver or BundledSQLiteDriver like suggested in the official migration guide the log message does not appear when using @Upsert, so this must have something to do with your SQLCipherDriver. Could you maybe look into that, because it's quite spammy?
This is not originating from the SQLCipherDriver itself as it is just delegating to the SQLCipher for Android internals. By default we log to logcat with exceptions, however, if you'd prefer you can set the log target to use the NoopTarget to remove that.
Activity
metzgore commentedon Oct 6, 2026
Hm, after some more testing the upsert seems to be working, but the exception is still logged 🤔
Also works when the primary key is a String and not auto generated...
Test project used:
MyApplication2.zip
developernotes commentedon Oct 6, 2026
Hi @metzgore,
We're glad to hear the upsert is working for you. From the structure of the exception log message, that appears to originate from Room internals.
metzgore commentedon Oct 7, 2026
When using the
AndroidSQLiteDriverorBundledSQLiteDriverlike suggested in the official migration guide the log message does not appear when using@Upsert, so this must have something to do with yourSQLCipherDriver. Could you maybe look into that, because it's quite spammy?developernotes commentedon Oct 7, 2026
Hi @metzgore,
This is not originating from the
SQLCipherDriveritself as it is just delegating to the SQLCipher for Android internals. By default we log to logcat with exceptions, however, if you'd prefer you can set the log target to use theNoopTargetto remove that.metzgore commentedon Oct 13, 2026
Still not sure why the exceptions are logged after the changes when they haven't before, but
NoopTargetwill do it for now I guess. Thanks!