{"record":{"id":"435ee31bc7f8be89","repo":"apache/druid","slug":"cannot-delete-d-bytes-only-have-d-buffere","errorCode":null,"errorMessage":"Cannot delete [%,d] bytes, only have [%,d] buffered","messagePattern":"Cannot delete \\[%,d\\] bytes, only have \\[%,d\\] buffered","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/frame/channel/ReadableByteChunksFrameChannel.java","lineNumber":500,"sourceCode":"      final int chunkPosition = chunkNumber == 0 ? positionInFirstChunk : 0;\n      final int len = Math.min(chunk.length - chunkPosition, numBytes - bufPos);\n\n      buf.putByteArray(bufPos, chunk, chunkPosition, len);\n      bufPos += len;\n\n      if (bufPos == numBytes) {\n        break;\n      }\n    }\n\n    return buf;\n  }\n\n  @GuardedBy(\"lock\")\n  private void deleteFromQueuedChunks(final long numBytes)\n  {\n    if (bytesBuffered < numBytes) {\n      throw new IAE(\"Cannot delete [%,d] bytes, only have [%,d] buffered\", numBytes, bytesBuffered);\n    }\n\n    long toDelete = numBytes;\n\n    while (toDelete > 0) {\n      final byte[] chunk = chunks.get(0).valueOrThrow();\n      final int bytesRemainingInChunk = chunk.length - positionInFirstChunk;\n\n      if (toDelete >= bytesRemainingInChunk) {\n        toDelete -= bytesRemainingInChunk;\n        positionInFirstChunk = 0;\n        chunks.remove(0);\n      } else {\n        positionInFirstChunk = Ints.checkedCast(positionInFirstChunk + toDelete);\n        toDelete = 0;\n      }\n    }\n","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/channel/ReadableByteChunksFrameChannel.java#L482-L518","documentation":"deleteFromQueuedChunks removes numBytes from the front of the buffered chunk queue. If fewer bytes are buffered than requested, it throws this IAE. Like its copy counterpart, this is an internal invariant: callers must only delete bytes known to be fully buffered. Violations imply races or misuse.","triggerScenarios":"deleteFromQueuedChunks(n) invoked when bytesBuffered < n — e.g. deleting the magic header or a consumed frame when buffering has already been drained by another caller.","commonSituations":"Concurrent readers consuming the same channel; custom wrappers bypassing the lock; internal bugs in stream-state accounting.","solutions":["Use the channel's public API from a single consumer only.","Audit any code that calls updateStreamState/delete paths concurrently.","Upgrade/report if reproducible on stock Druid paths.","Capture the id and buffered counts in logs to diagnose the race."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { rac = channel.read(); } catch (IAE e) { if (e.getMessage().startsWith(\"Cannot delete\")) { reportInternalBug(e); } else { throw e; } }","preventionTips":["Single consumer per channel instance.","Do not interleave manual state manipulation with reads.","Report stock-path reproductions upstream."],"tags":["druid","frame-channel","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}