google/ExoPlayer · error · IllegalArgumentException
Passed buffer is not a direct ByteBuffer
Error message
Passed buffer is not a direct ByteBuffer
What it means
IllegalArgumentException thrown by CronetDataSource.read(ByteBuffer) when the supplied buffer is not a direct (off-heap) ByteBuffer. Cronet writes response bytes natively into the buffer, which requires native-addressable memory; heap ByteBuffers have no native pointer, so the API rejects them up front (documented on the method).
Source
Thrown at extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java:734
* because the end of the opened range has been reached, then {@link C#RESULT_END_OF_INPUT} is
* returned. Otherwise, the call will block until at least one byte of data has been read and the
* number of bytes read is returned.
*
* <p>Passed buffer must be direct ByteBuffer. If you have a non-direct ByteBuffer, consider the
* alternative read method with its backed array.
*
* @param buffer The ByteBuffer into which the read data should be stored. Must be a direct
* ByteBuffer.
* @return The number of bytes read, or {@link C#RESULT_END_OF_INPUT} if no data is available
* because the end of the opened range has been reached.
* @throws HttpDataSourceException If an error occurs reading from the source.
* @throws IllegalArgumentException If {@code buffer} is not a direct ByteBuffer.
*/
public int read(ByteBuffer buffer) throws HttpDataSourceException {
Assertions.checkState(opened);
if (!buffer.isDirect()) {
throw new IllegalArgumentException("Passed buffer is not a direct ByteBuffer");
}
if (!buffer.hasRemaining()) {
return 0;
} else if (bytesRemaining == 0) {
return C.RESULT_END_OF_INPUT;
}
int readLength = buffer.remaining();
if (readBuffer != null) {
// If there is existing data in the readBuffer, read as much as possible. Return if any read.
int copyBytes = copyByteBuffer(/* src= */ readBuffer, /* dst= */ buffer);
if (copyBytes != 0) {
if (bytesRemaining != C.LENGTH_UNSET) {
bytesRemaining -= copyBytes;
}
bytesTransferred(copyBytes);
return copyBytes;
}View on GitHub (pinned to dd430f7053)
Solutions
- Allocate with ByteBuffer.allocateDirect(capacity) for every buffer passed to read(ByteBuffer)
- Reuse a single direct buffer and flip/clear it between reads to avoid repeated native allocations
- If you need byte[], use DataSource.read(byte[], int, int) instead, which copies internally
- Wrap the call: assert buffer.isDirect() in debug builds to catch mistakes early
Example fix
// before ByteBuffer buffer = ByteBuffer.allocate(64 * 1024); int read = dataSource.read(buffer); // throws IllegalArgumentException // after ByteBuffer buffer = ByteBuffer.allocateDirect(64 * 1024); int read = dataSource.read(buffer);
Defensive patterns
Strategy: type-guard
Validate before calling
ByteBuffer buffer = ByteBuffer.allocateDirect(64 * 1024); // direct by construction; keep one buffer and flip()/clear() between reads
Type guard
static boolean isUsableByCronet(ByteBuffer buffer) {
return buffer != null && buffer.isDirect() && buffer.hasRemaining();
}
// usage
if (!isUsableByCronet(buf)) {
ByteBuffer direct = ByteBuffer.allocateDirect(buf.capacity());
direct.put(buf).flip();
buf = direct; // converted heap -> direct
} Try / catch
try {
int read = dataSource.read(buffer);
} catch (IllegalArgumentException e) {
if (buffer.isDirect()) throw e; // different IAE
buffer = ByteBuffer.allocateDirect(buffer.capacity()); // fix and retry
int read = dataSource.read(buffer);
} Prevention
- Always allocate buffers for CronetDataSource.read(ByteBuffer) with allocateDirect
- Prefer DataSource.read(byte[], int, int) when you ultimately need byte[] anyway
- Assert buffer.isDirect() in debug builds to catch accidental heap buffers early
When it happens
Trigger: Calling dataSource.read(ByteBuffer.allocate(n)) or wrapping a byte[] (both heap buffers) with the CronetDataSource ByteBuffer overload; adapting a DataSource into a NIO-based consumer that allocates heap buffers; test code reusing standard buffers.
Common situations: Wrapping CronetDataSource in custom download/cache code that uses ByteBuffer.allocate; porting code from a DataSource.read(byte[],...) loop into the ByteBuffer API without switching allocation.
Related errors
- Invalid output mode.
- Failed to render output buffer to surface: decoder is not in
- ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED
- ERROR_CODE_IO_BAD_HTTP_STATUS
- ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE
AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14).
Data as JSON: /api/errors/0b2a27260073ba80.
Report an issue: GitHub.