MuntashirAkon/AppManager · error · IOException

Direct buffer somehow written to BufferAtATimeOutputChannel

Error message

Direct buffer somehow written to BufferAtATimeOutputChannel

What it means

The BufferAtATimeOutputChannel inner class of FixedLengthBlockOutputStream only supports heap-backed ByteBuffers: it copies via buffer.array(). Direct (off-heap) buffers have no accessible array, so writing one throws this IOException as an internal contract violation.

Solutions

  1. Wrap the direct buffer's contents into a heap buffer first (allocate a heap buffer, put the direct buffer's remaining bytes, then write)
  2. Use ByteBuffer.allocate() instead of allocateDirect() for buffers destined for this stream
  3. Copy data to a byte[] and use the array-based write

Example fix

// before
channel.write(directBuffer); // IOException: buffer.hasArray() == false
// after
ByteBuffer heap = ByteBuffer.allocate(directBuffer.remaining());
heap.put(directBuffer.duplicate());
heap.flip();
channel.write(heap);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!buffer.isDirect() && buffer.hasArray()) { channel.write(buffer); } else { /* copy to heap buffer first */ }

Type guard

boolean isWritableByBufferAtATime(ByteBuffer b) { return b != null && !b.isDirect() && b.hasArray(); }

Try / catch

try { channel.write(buffer); } catch (IOException e) { if (e.getMessage().contains("Direct buffer")) { ByteBuffer heap = ByteBuffer.allocate(buffer.remaining()); heap.put(buffer.duplicate()).flip(); channel.write(heap); } else throw e; }

Prevention

When it happens

Trigger: Calling write(ByteBuffer) on BufferAtATimeOutputChannel with a direct ByteBuffer allocated via ByteBuffer.allocateDirect(...) (e.g. obtained from NIO pooling or JNI code).

Common situations: Passing pooled direct buffers from network code into the block stream; assuming all ByteBuffers are array-backed; refactoring code from heap to direct buffers for performance.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12). Data as JSON: /api/errors/252b09375668799c. Report an issue: GitHub.

Appendix: source

Thrown at app/src/main/java/org/apache/commons/compress/utils/FixedLengthBlockOutputStream.java:223

     * breaks up writes into 8KB max chunks. Since the purpose of this class is to always write
     * complete blocks, we need to write a simple class to take care of it.
     */
    private static class BufferAtATimeOutputChannel implements WritableByteChannel {

        private final OutputStream out;
        private final AtomicBoolean closed = new AtomicBoolean(false);

        private BufferAtATimeOutputChannel(final OutputStream out) {
            this.out = out;
        }

        @Override
        public int write(final ByteBuffer buffer) throws IOException {
            if (!isOpen()) {
                throw new ClosedChannelException();
            }
            if (!buffer.hasArray()) {
                throw new IOException("Direct buffer somehow written to BufferAtATimeOutputChannel");
            }

            try {
                final int pos = buffer.position();
                final int len = buffer.limit() - pos;
                out.write(buffer.array(), buffer.arrayOffset() + pos, len);
                buffer.position(buffer.limit());
                return len;
            } catch (final IOException e) {
                try {
                    close();
                } catch (final IOException ignored) { //NOSONAR
                }
                throw e;
            }
        }

        @Override

View on GitHub (pinned to 0152f468fc)