{"record":{"id":"7cb0c7cb5da76d9d","repo":"Tencent/matrix","slug":"getdatabase-called-recursively","errorCode":null,"errorMessage":"getDatabase called recursively","messagePattern":"getDatabase called recursively","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"matrix/matrix-android/matrix-sqlite-lint/matrix-sqlite-lint-android-sdk/src/full/java/com/tencent/sqlitelint/behaviour/persistence/SQLiteLintOwnDatabase.java","lineNumber":111,"sourceCode":"    }\n\n    private void onCreate(SQLiteDatabase db) {\n        SLog.i(TAG, \"onCreate\");\n\n        db.execSQL(IssueStorage.DB_VERSION_1_CREATE_SQL);\n        for (int i = 0; i < IssueStorage.DB_VERSION_1_CREATE_INDEX.length; i++) {\n            db.execSQL(IssueStorage.DB_VERSION_1_CREATE_INDEX[i]);\n        }\n    }\n\n    private void onUpgrade(SQLiteDatabase db, int oldVersion) {\n        SLog.i(TAG, \"onUpgrade oldVersion=%d, newVersion=%d\", oldVersion, NEW_VERSION);\n    }\n\n    /* only called in getDatabase synchronize block */\n    private SQLiteDatabase openOrCreateDatabase() {\n        if (mIsInitializing) {\n            throw new IllegalStateException(\"getDatabase called recursively\");\n        }\n\n        if (SQLiteLintUtil.isNullOrNil(sOwnDbDirectory)) {\n            throw new IllegalStateException(\"OwnDbDirectory not init\");\n        }\n\n        try {\n            mIsInitializing = true;\n\n            String databasePath = String.format(\"%s/%s\", sOwnDbDirectory, DATABASE_NAME);\n            SLog.i(TAG, \"openOrCreateDatabase path=%s\", databasePath);\n            SQLiteLintUtil.mkdirs(databasePath);\n            SQLiteDatabase db = SQLiteDatabase.openDatabase(databasePath, null, SQLiteDatabase.CREATE_IF_NECESSARY, null);\n            final int version = db.getVersion();\n            if (version != NEW_VERSION) {\n                db.beginTransaction();\n                try {\n                    if (version == 0) {","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/Tencent/matrix/blob/3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7/matrix/matrix-android/matrix-sqlite-lint/matrix-sqlite-lint-android-sdk/src/full/java/com/tencent/sqlitelint/behaviour/persistence/SQLiteLintOwnDatabase.java#L93-L129","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n@Override public void onUpgrade(SQLiteDatabase db, int o, int n) {\n    ownDatabase.getDatabase(); // recursive open\n}\n// after\n@Override public void onUpgrade(SQLiteDatabase db, int o, int n) {\n    postInitTasks.add(() -> refreshIssueCache()); // run after open completes\n}","handlingStrategy":"validation","validationCode":"// never call getDatabase inside onCreate/onUpgrade\nif (insideDbCallback) { queueAfterInit(task); }","typeGuard":null,"tryCatchPattern":"try {\n    db = ownDatabase.getDatabase();\n} catch (IllegalStateException e) {\n    // recursive open detected; defer task until current open completes\n}","preventionTips":["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()"],"tags":["sqlite","reentrancy","concurrency","initialization"],"backgroundTag":"internal-invariant-violation","analyzedSha":"3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7","analyzedAt":"2026-09-08T08:01:39.722Z","contentChangedAt":"2026-09-08T08:01:39.722Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}