greenrobot/greenDAO · error · DaoException
Internal error: peeked op did not match removed op
Error message
Internal error: peeked op did not match removed op
What it means
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).
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.
Example fix
// before // async ops enqueued from multiple worker threads on the same asyncSession new Thread(() -> asyncSession.insert(a)).start(); new Thread(() -> asyncSession.insert(b)).start(); // after // funnel all async ops through one thread/executor singleThreadExecutor.execute(() -> asyncSession.insert(a)); singleThreadExecutor.execute(() -> asyncSession.insert(b));
Defensive patterns
Strategy: fallback
Validate before calling
// enqueue all async ops for a session from a single thread:
Executor asyncEnqueuer = Executors.newSingleThreadExecutor();
if (!asyncEnqueuer.isShutdown()) { asyncEnqueuer.execute(() -> session.insert(e)); } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- Interrupted while waiting for operation to complete
- Interrupted while waiting for all operations to complete
- This operation did not yet complete
- Unsupported operation
- Method may be called only in owner thread, use…
AI-assisted analysis of greenrobot/greenDAO@0bbb338e17 (2026-09-08).
Data as JSON: /api/errors/c50d982ac49e704c.
Report an issue: GitHub.
Appendix: source
Thrown at DaoCore/src/main/java/org/greenrobot/greendao/async/AsyncOperationExecutor.java:201
Database db = operation1.getDatabase();
db.beginTransaction();
boolean success = false;
try {
for (int i = 0; i < mergedOps.size(); i++) {
AsyncOperation operation = mergedOps.get(i);
executeOperation(operation);
if (operation.isFailed()) {
// Operation may still have changed the DB, roll back everything
break;
}
if (i == mergedOps.size() - 1) {
AsyncOperation peekedOp = queue.peek();
if (i < maxOperationCountToMerge && operation.isMergeableWith(peekedOp)) {
AsyncOperation removedOp = queue.remove();
if (removedOp != peekedOp) {
// Paranoia check, should not occur unless threading is broken
throw new DaoException("Internal error: peeked op did not match removed op");
}
mergedOps.add(removedOp);
} else {
// No more ops in the queue to merge, finish it
db.setTransactionSuccessful();
success = true;
break;
}
}
}
} finally {
try {
db.endTransaction();
} catch (RuntimeException e) {
DaoLog.i("Async transaction could not be ended, success so far was: " + success, e);
success = false;
}
}View on GitHub (pinned to 0bbb338e17)