{"record":{"id":"474e63f99b128b54","repo":"bumptech/glide","slug":"this-bufferqueue-has-already-been-consumed","errorCode":null,"errorMessage":"This BufferQueue has already been consumed","messagePattern":"This BufferQueue has already been consumed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/BufferQueue.java","lineNumber":133,"sourceCode":"    } else if (buffers.size() == 1) {\n      return buffers.remove();\n    } else {\n      int size = 0;\n      for (ByteBuffer buffer : buffers) {\n        size += buffer.remaining();\n      }\n      ByteBuffer result = ByteBuffer.allocateDirect(size);\n      while (!buffers.isEmpty()) {\n        result.put(buffers.remove());\n      }\n      result.flip();\n      return result;\n    }\n  }\n\n  private void markCoalesced() {\n    if (!isCoalesced.compareAndSet(false, true)) {\n      throw new IllegalStateException(\"This BufferQueue has already been consumed\");\n    }\n  }\n}\n","sourceCodeStart":115,"sourceCodeEnd":137,"githubUrl":"https://github.com/bumptech/glide/blob/eb14a895d8f866e6a08c3e19d50ea3bd2c12c20a/integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/BufferQueue.java#L115-L137","documentation":"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.","triggerScenarios":"coalesceToBuffer() (or any path calling markCoalesced) is invoked more than once on the same BufferQueue instance, e.g. reading the Cronet response body twice.","commonSituations":"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.","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."],"exampleFix":"// before\nval body1 = queue.coalesceToBuffer()\nval body2 = queue.coalesceToBuffer() // throws: already consumed\n\n// after\nval body = queue.coalesceToBuffer()\nbody.rewind()\nval copy = body.duplicate() // share without re-consuming","handlingStrategy":"validation","validationCode":"// Track consumption; read each BufferQueue exactly once.\nvar consumed = false\nfun readOnce(queue: BufferQueue): ByteBuffer {\n  check(!consumed) { \"BufferQueue already consumed\" }\n  consumed = true\n  return queue.coalesceToBuffer()\n}","typeGuard":null,"tryCatchPattern":"// If double-reads are possible, catch and recover by retrying the request.\ntry {\n  body = queue.coalesceToBuffer()\n} catch (e: IllegalStateException) {\n  // re-issue the request to obtain a fresh BufferQueue\n  requestAgain()\n}","preventionTips":["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."],"tags":["cronet","integration","runtime","double-consume","buffer"],"backgroundTag":null,"analyzedSha":"eb14a895d8f866e6a08c3e19d50ea3bd2c12c20a","analyzedAt":"2026-08-14T01:43:54.821Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}