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
- Wrap the direct buffer's contents into a heap buffer first (allocate a heap buffer, put the direct buffer's remaining bytes, then write)
- Use ByteBuffer.allocate() instead of allocateDirect() for buffers destined for this stream
- 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
- Only pass heap ByteBuffers (ByteBuffer.allocate) to this channel
- Copy direct buffers into heap buffers before writing
- Watch for pooled/borrowed direct buffers from NIO/JNI layers
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- ADB command did not produce
- APK files backup is requested but no APK files have been…
- APPEND + TRUNCATE not allowed
- Array Size=" + array.length + ", offset=" + off + "…
- Backup failed for
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;
}
}
@OverrideView on GitHub (pinned to 0152f468fc)