bumptech/glide · error · IllegalStateException
Failed to delete entry, key: {}, size: {}, actually deleted:
Error message
Failed to delete entry, key: {}, size: {}, actually deleted: {} What it means
Thrown by Journal during a single-key delete when executeUpdateDelete() does not delete exactly one row. The journal expects the targeted key to resolve to exactly one deletable row; a result other than 1 means the row was absent (or duplicated), breaking the size-accounting invariant.
Source
Thrown at integration/sqljournaldiskcache/src/main/java/com/bumptech/glide/integration/sqljournaldiskcache/Journal.java:307
SizeSQLiteTransactionListener sizeListener = sizeJournal.prepareSizeTransaction();
db.beginTransactionWithListenerNonExclusive(sizeListener);
try {
// We may be asked to abort a put that failed before the entry was updated, so we will
// occasionally fail to find the entry here.
boolean isKeyPresent = 0 != containsKeyStatement.simpleQueryForLong();
if (!isKeyPresent) {
return;
}
long entrySize;
try {
entrySize = selectNotPendingSizeStatement.simpleQueryForLong();
} catch (SQLiteDoneException e) {
// No row found for this key that is not pending delete.
entrySize = 0;
}
int deleted = deleteEntryStatement.executeUpdateDelete();
if (deleted != 1) {
throw new IllegalStateException(
"Failed to delete entry"
+ ", key: "
+ key
+ ", size: "
+ entrySize
+ ", actually deleted: "
+ deleted);
} else {
// If the item is pending delete its size is 0 here - skip decrementing.
if (entrySize != 0) {
sizeJournal.decrementSizeInTransaction(sizeListener, entrySize);
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
statementPool.offer(CONTAINS_KEY_SQL, containsKeyStatement);
statementPool.offer(SELECT_ENTRY_SIZE_NOT_PENDING_SQL, selectNotPendingSizeStatement);View on GitHub (pinned to eb14a895d8)
Solutions
- Clear the cache to reset the journal and eliminate any duplicated/inconsistent rows.
- Confirm only one EvictionManager/disk-cache instance operates on the directory concurrently.
- Upgrade the sqljournaldiskcache integration for concurrency fixes around delete.
- If reproducible, capture the key, expected size, and actual deleted count from the message and file a bug.
Defensive patterns
Strategy: fallback
Try / catch
try {
diskCache.delete(key)
} catch (e: IllegalStateException) {
if (e.message?.contains("Failed to delete entry") == true) {
// row already gone or duplicated; clear to reset consistent state
diskCache.clear()
} else throw e
} Prevention
- Ensure only one cache instance operates on a directory.
- Clear the cache if delete-count mismatches recur (signals duplicated/inconsistent rows).
- Keep the integration updated for concurrency fixes.
When it happens
Trigger: selectNotPendingSizeStatement confirms the key is present (isKeyPresent true), but the subsequent deleteEntryStatement.executeUpdateDelete() returns a count != 1, so the guard at line 307 fires.
Common situations: A race where the row was removed between the presence check and the delete; duplicated rows from a prior partial write; concurrent eviction and put operating on the same key; a journal corruption causing the presence query and delete to disagree.
Related errors
- Failed to find entries to evict.
- Size mismatch, expected to be able to evict at least {} byte
- Cannot call evictOnWorkThread on thread: {}
- Failed to create cache directory: {}
- Cannot run recovery on a thread other than the work thread!
AI-assisted analysis of bumptech/glide@eb14a895d8 (2026-08-14).
Data as JSON: /api/errors/29a72d27229a722a.
Report an issue: GitHub.