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

  1. Allocate with ByteBuffer.allocateDirect(capacity) for every buffer passed to read(ByteBuffer)
  2. Reuse a single direct buffer and flip/clear it between reads to avoid repeated native allocations
  3. If you need byte[], use DataSource.read(byte[], int, int) instead, which copies internally
  4. 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

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


AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14). Data as JSON: /api/errors/0b2a27260073ba80. Report an issue: GitHub.