{"record":{"id":"c50d982ac49e704c","repo":"greenrobot/greenDAO","slug":"internal-error-peeked-op-did-not-match-removed-op","errorCode":null,"errorMessage":"Internal error: peeked op did not match removed op","messagePattern":"Internal error: peeked op did not match removed op","errorType":"exception","errorClass":"DaoException","httpStatus":null,"severity":"error","filePath":"DaoCore/src/main/java/org/greenrobot/greendao/async/AsyncOperationExecutor.java","lineNumber":201,"sourceCode":"\n        Database db = operation1.getDatabase();\n        db.beginTransaction();\n        boolean success = false;\n        try {\n            for (int i = 0; i < mergedOps.size(); i++) {\n                AsyncOperation operation = mergedOps.get(i);\n                executeOperation(operation);\n                if (operation.isFailed()) {\n                    // Operation may still have changed the DB, roll back everything\n                    break;\n                }\n                if (i == mergedOps.size() - 1) {\n                    AsyncOperation peekedOp = queue.peek();\n                    if (i < maxOperationCountToMerge && operation.isMergeableWith(peekedOp)) {\n                        AsyncOperation removedOp = queue.remove();\n                        if (removedOp != peekedOp) {\n                            // Paranoia check, should not occur unless threading is broken\n                            throw new DaoException(\"Internal error: peeked op did not match removed op\");\n                        }\n                        mergedOps.add(removedOp);\n                    } else {\n                        // No more ops in the queue to merge, finish it\n                        db.setTransactionSuccessful();\n                        success = true;\n                        break;\n                    }\n                }\n            }\n        } finally {\n            try {\n                db.endTransaction();\n            } catch (RuntimeException e) {\n                DaoLog.i(\"Async transaction could not be ended, success so far was: \" + success, e);\n                success = false;\n            }\n        }","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/greenrobot/greenDAO/blob/0bbb338e17c4cf28aba5aae62d42f73f50186157/DaoCore/src/main/java/org/greenrobot/greendao/async/AsyncOperationExecutor.java#L183-L219","documentation":"During transaction merging, AsyncOperationExecutor peeks the queue head and then removes it, expecting the same operation. If remove() returns a different op than peek() saw, the queue's internal consistency is broken — greendao's own comment calls it a paranoia check for broken threading. This indicates a concurrency bug (queue mutated by another thread during merge).","triggerScenarios":"Multiple threads concurrently enqueueing/removing operations on the same AsyncOperationExecutor while a transaction merge (mergeable ops like InsertInTxIterable) is in progress, or misuse of the executor's internal queue from outside its design.","commonSituations":"Rare; usually means the app is sharing an AsyncSession/executor across threads in a way greendao didn't anticipate, or a greendao version bug in the merge path.","solutions":["Serialize async access: issue async operations from a single thread, or guard enqueue calls with your own synchronization.","Check your greendao version and upgrade — this path was hardened in later releases.","If reproducible, file a bug with a thread dump; do not attempt to work around it by catching it.","Reduce use of mergeable multi-op transactions (e.g. many insertInTxAsync calls) from different threads."],"exampleFix":"// before\n// async ops enqueued from multiple worker threads on the same asyncSession\nnew Thread(() -> asyncSession.insert(a)).start();\nnew Thread(() -> asyncSession.insert(b)).start();\n// after\n// funnel all async ops through one thread/executor\nsingleThreadExecutor.execute(() -> asyncSession.insert(a));\nsingleThreadExecutor.execute(() -> asyncSession.insert(b));","handlingStrategy":"fallback","validationCode":"// enqueue all async ops for a session from a single thread:\nExecutor asyncEnqueuer = Executors.newSingleThreadExecutor();\nif (!asyncEnqueuer.isShutdown()) { asyncEnqueuer.execute(() -> session.insert(e)); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Enqueue async operations from one thread (or synchronize enqueue calls)","Keep greendao up to date — the merge path has had fixes","If this reproduces, capture a thread dump and report it; it signals a real concurrency bug"],"tags":["async","threading","internal-error"],"backgroundTag":"internal-invariant-violation","analyzedSha":"0bbb338e17c4cf28aba5aae62d42f73f50186157","analyzedAt":"2026-09-08T03:22:36.050Z","contentChangedAt":"2026-09-08T03:22:36.050Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}