Tencent/matrix · error · IllegalStateException
getDatabase called recursively
Error message
getDatabase called recursively
What it means
SQLiteLintOwnDatabase.openOrCreateDatabase is private and must only run inside getDatabase's synchronized block, which sets mIsInitializing around it. If it is entered while mIsInitializing is already true, it throws IllegalStateException('getDatabase called recursively'), detecting re-entrant or concurrent invocation of the open path.
Solutions
- Never call getDatabase (or any issue-persistence API) from inside onCreate/onUpgrade callbacks; defer such work to after init completes.
- Use a single background thread/executor for all SQLiteLint db access so opens are serialized.
- Ensure access goes through getDatabase's synchronized path, not direct calls to openOrCreateDatabase.
- Move upgrade-time migrations out of callbacks or make them re-entrancy-safe.
Example fix
// before
@Override public void onUpgrade(SQLiteDatabase db, int o, int n) {
ownDatabase.getDatabase(); // recursive open
}
// after
@Override public void onUpgrade(SQLiteDatabase db, int o, int n) {
postInitTasks.add(() -> refreshIssueCache()); // run after open completes
} Defensive patterns
Strategy: validation
Validate before calling
// never call getDatabase inside onCreate/onUpgrade
if (insideDbCallback) { queueAfterInit(task); } Try / catch
try {
db = ownDatabase.getDatabase();
} catch (IllegalStateException e) {
// recursive open detected; defer task until current open completes
} Prevention
- Never query the lint db from inside database callbacks
- Use a single-thread executor for all db access
- Only enter open paths via the public getDatabase()
When it happens
Trigger: A re-entrant call into openOrCreateDatabase while an outer getDatabase/openOrCreateDatabase is still running — e.g. an onUpgrade/onCreate callback that itself calls getDatabase, or a second thread entering the open path without the outer sync completing.
Common situations: Database upgrade callbacks (onUpgrade) triggering code that queries SQLiteLint issues again; two threads racing the first open when the synchronized discipline is bypassed; custom code reflecting into the private method.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- getIssueStorage db not ready
- Closed during initialization
- OwnDbDirectory not init
- Matrix init, Matrix should not be null.
- you must init Matrix sdk first
AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08).
Data as JSON: /api/errors/7cb0c7cb5da76d9d.
Report an issue: GitHub.
Appendix: source
Thrown at matrix/matrix-android/matrix-sqlite-lint/matrix-sqlite-lint-android-sdk/src/full/java/com/tencent/sqlitelint/behaviour/persistence/SQLiteLintOwnDatabase.java:111
}
private void onCreate(SQLiteDatabase db) {
SLog.i(TAG, "onCreate");
db.execSQL(IssueStorage.DB_VERSION_1_CREATE_SQL);
for (int i = 0; i < IssueStorage.DB_VERSION_1_CREATE_INDEX.length; i++) {
db.execSQL(IssueStorage.DB_VERSION_1_CREATE_INDEX[i]);
}
}
private void onUpgrade(SQLiteDatabase db, int oldVersion) {
SLog.i(TAG, "onUpgrade oldVersion=%d, newVersion=%d", oldVersion, NEW_VERSION);
}
/* only called in getDatabase synchronize block */
private SQLiteDatabase openOrCreateDatabase() {
if (mIsInitializing) {
throw new IllegalStateException("getDatabase called recursively");
}
if (SQLiteLintUtil.isNullOrNil(sOwnDbDirectory)) {
throw new IllegalStateException("OwnDbDirectory not init");
}
try {
mIsInitializing = true;
String databasePath = String.format("%s/%s", sOwnDbDirectory, DATABASE_NAME);
SLog.i(TAG, "openOrCreateDatabase path=%s", databasePath);
SQLiteLintUtil.mkdirs(databasePath);
SQLiteDatabase db = SQLiteDatabase.openDatabase(databasePath, null, SQLiteDatabase.CREATE_IF_NECESSARY, null);
final int version = db.getVersion();
if (version != NEW_VERSION) {
db.beginTransaction();
try {
if (version == 0) {View on GitHub (pinned to 3b8293bd65)