{"record":{"id":"f9b1017c1eabdb63","repo":"apache/druid","slug":"cannot-copy-d-bytes-only-have-d-buffered","errorCode":null,"errorMessage":"Cannot copy [%,d] bytes, only have [%,d] buffered","messagePattern":"Cannot copy \\[%,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":474,"sourceCode":"\n  @GuardedBy(\"lock\")\n  private boolean canReadError()\n  {\n    return chunks.size() > 0 && chunks.get(0).isError();\n  }\n\n  @GuardedBy(\"lock\")\n  private boolean canReadFrame()\n  {\n    return nextCompressedFrameLength != UNKNOWN_LENGTH\n           && bytesBuffered >= FRAME_MARKER_AND_COMPRESSED_ENVELOPE_BYTES + nextCompressedFrameLength;\n  }\n\n  @GuardedBy(\"lock\")\n  private Memory copyFromQueuedChunks(final int numBytes)\n  {\n    if (bytesBuffered < numBytes) {\n      throw new IAE(\"Cannot copy [%,d] bytes, only have [%,d] buffered\", numBytes, bytesBuffered);\n    }\n\n    final WritableMemory buf = WritableMemory.allocate(numBytes, ByteOrder.LITTLE_ENDIAN);\n\n    int bufPos = 0;\n    for (int chunkNumber = 0; chunkNumber < chunks.size(); chunkNumber++) {\n      final byte[] chunk = chunks.get(chunkNumber).valueOrThrow();\n      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","sourceCodeStart":456,"sourceCodeEnd":492,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/channel/ReadableByteChunksFrameChannel.java#L456-L492","documentation":"copyFromQueuedChunks copies a requested number of bytes out of the buffered chunks. If fewer bytes are buffered than requested, it throws this IAE — an internal invariant failure, since callers should only request fully-buffered byte counts. It indicates a bug or external misuse (e.g., non-synchronized access) rather than a normal runtime condition.","triggerScenarios":"Calling copyFromQueuedChunks(n) when bytesBuffered < n — possible via direct misuse of the channel internals or a race where chunks were consumed/deleted before the copy.","commonSituations":"Custom code subclassing or wrapping the channel without holding lock; concurrent addChunk/deleteFromQueuedChunks interleaving due to broken external synchronization; Druid internal bugs.","solutions":["Ensure all interactions with the channel occur through its public thread-safe API, not by reaching into internals.","Check for concurrent access from multiple threads to the same channel instance.","Upgrade Druid if reproducible on a stock path (likely an internal bug to report).","Add logging around addChunk/read to capture the interleaving."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { rac = channel.read(); } catch (IAE e) { if (e.getMessage().startsWith(\"Cannot copy\")) { reportInternalBug(e); } else { throw e; } }","preventionTips":["Never access channel internals outside the lock/public API.","Restrict each channel to one consumer thread.","Report reproducible occurrences as Druid bugs with logs."],"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"}