bumptech/glide · error · IllegalStateException
This BufferQueue has already been consumed
Error message
This BufferQueue has already been consumed
What it means
Thrown by the Cronet integration's BufferQueue.markCoalesced() when the queue has already been consumed once. BufferQueue uses an AtomicBoolean (isCoalesced) flipped from false to true via compareAndSet; a second coalesce/consume attempt fails the CAS and aborts, because the underlying buffers have already been drained.
Source
Thrown at integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/BufferQueue.java:133
} else if (buffers.size() == 1) {
return buffers.remove();
} else {
int size = 0;
for (ByteBuffer buffer : buffers) {
size += buffer.remaining();
}
ByteBuffer result = ByteBuffer.allocateDirect(size);
while (!buffers.isEmpty()) {
result.put(buffers.remove());
}
result.flip();
return result;
}
}
private void markCoalesced() {
if (!isCoalesced.compareAndSet(false, true)) {
throw new IllegalStateException("This BufferQueue has already been consumed");
}
}
}
View on GitHub (pinned to eb14a895d8)
Solutions
- Consume each BufferQueue exactly once; cache the resulting ByteBuffer if you need the data in multiple places.
- If multiple consumers need the body, coalesce once and copy/distribute the resulting buffer.
- Ensure interceptors do not double-read the body; wrap with a single read-and-broadcast pattern.
- Reset the request so a fresh BufferQueue is produced for a true retry.
Example fix
// before val body1 = queue.coalesceToBuffer() val body2 = queue.coalesceToBuffer() // throws: already consumed // after val body = queue.coalesceToBuffer() body.rewind() val copy = body.duplicate() // share without re-consuming
Defensive patterns
Strategy: validation
Validate before calling
// Track consumption; read each BufferQueue exactly once.
var consumed = false
fun readOnce(queue: BufferQueue): ByteBuffer {
check(!consumed) { "BufferQueue already consumed" }
consumed = true
return queue.coalesceToBuffer()
} Try / catch
// If double-reads are possible, catch and recover by retrying the request.
try {
body = queue.coalesceToBuffer()
} catch (e: IllegalStateException) {
// re-issue the request to obtain a fresh BufferQueue
requestAgain()
} Prevention
- Coalesce the Cronet body once and share copies/duplicates.
- Make body-reading interceptors single-pass and broadcast the result.
- Treat a second coalesce attempt as a programming error to fix at the call site.
When it happens
Trigger: coalesceToBuffer() (or any path calling markCoalesced) is invoked more than once on the same BufferQueue instance, e.g. reading the Cronet response body twice.
Common situations: An interceptor or middleware that re-reads the Cronet response body; retrying a request that reuses the same buffer queue; a logging layer that buffers the body for inspection then the real consumer reads it again.
Related errors
- Cannot call evictOnWorkThread on thread: {}
- Failed to find entries to evict.
- Size mismatch, expected to be able to evict at least {} byte
- Failed to delete entry, key: {}, size: {}, actually deleted:
- Failed to create cache directory: {}
AI-assisted analysis of bumptech/glide@eb14a895d8 (2026-08-14).
Data as JSON: /api/errors/474e63f99b128b54.
Report an issue: GitHub.