libgdx/libgdx · error · ReadOnlyBufferException
ReadOnlyBufferException
Error message
ReadOnlyBufferException
What it means
Thrown by FloatToByteBufferAdapter.compact() when the underlying ByteBuffer is read-only. This adapter wraps a ByteBuffer to present it as a FloatBuffer; compacting moves data within the backing store, which is illegal on a buffer obtained via asReadOnlyBuffer(). The check mirrors the JDK contract that compact() throws ReadOnlyBufferException on read-only buffers.
Source
Thrown at backends/gdx-backends-gwt/src/com/badlogic/gdx/backends/gwt/emu/java/nio/FloatToByteBufferAdapter.java:108
// ((DirectBuffer) byteBuffer).free();
// } else {
// assert false : byteBuffer;
// }
// }
@Override
public FloatBuffer asReadOnlyBuffer () {
FloatToByteBufferAdapter buf = new FloatToByteBufferAdapter(byteBuffer.asReadOnlyBuffer());
buf.limit = limit;
buf.position = position;
buf.mark = mark;
return buf;
}
@Override
public FloatBuffer compact () {
if (byteBuffer.isReadOnly()) {
throw new ReadOnlyBufferException();
}
byteBuffer.limit(limit << 2);
byteBuffer.position(position << 2);
byteBuffer.compact();
byteBuffer.clear();
position = limit - position;
limit = capacity;
mark = UNSET_MARK;
return this;
}
@Override
public FloatBuffer duplicate () {
FloatToByteBufferAdapter buf = new FloatToByteBufferAdapter(byteBuffer.duplicate());
buf.limit = limit;
buf.position = position;
buf.mark = mark;
return buf;View on GitHub (pinned to 97f4086187)
Solutions
- Compact the original writable FloatBuffer, not the read-only view: keep a reference to the writable buffer and only expose asReadOnlyBuffer() copies to consumers.
- Check isReadOnly() before compacting: if (!buf.isReadOnly()) buf.compact();
- Rebuild the buffer contents into a fresh writable buffer (FloatBuffer.allocate + put) instead of compacting the read-only one.
- Wrap the compact() call in try-catch (ReadOnlyBufferException) and fall back to reallocating.
Example fix
// before
FloatBuffer ro = writable.asReadOnlyBuffer();
ro.compact(); // ReadOnlyBufferException
// after
if (!ro.isReadOnly()) {
ro.compact();
} else {
writable.compact(); // compact the original writable buffer
} Defensive patterns
Strategy: validation
Validate before calling
// before buf.compact()
if (buf.isReadOnly()) {
throw new IllegalStateException("cannot compact a read-only buffer");
} Try / catch
try { buf.compact(); } catch (ReadOnlyBufferException e) { /* fall back to copy into a new writable buffer */ } Prevention
- Keep the writable master buffer as the owner; only hand out asReadOnlyBuffer() copies.
- Check isReadOnly() before any state-mutating operation (compact, put).
- Treat read-only views as consumed data, never as scratch space for reuse.
When it happens
Trigger: Calling compact() on a FloatBuffer returned by FloatToByteBufferAdapter.asReadOnlyBuffer(), or on a FloatBuffer view whose underlying ByteBuffer was itself created from a read-only byte buffer.
Common situations: Chaining asReadOnlyBuffer() to hand a buffer to untrusted rendering code, then later calling compact() on the same view during buffer reuse in a GWT mesh uploader; sharing a read-only byte buffer between threads and trying to compact after partial consumption.
Related errors
- libGDX requires Android API Level " + MINIMUM_SDK + " or lat
- BufferUnderflowException
- IndexOutOfBoundsException
- UnsupportedOperationException
- BufferOverflowException
AI-assisted analysis of libgdx/libgdx@97f4086187 (2026-08-14).
Data as JSON: /api/errors/2f7f1127d2379151.
Report an issue: GitHub.