grpc/grpc-java · error · IndexOutOfBoundsException
EOF trying to read ${length} bytes
Error message
EOF trying to read ${length} bytes What it means
OkHttpReadableBuffer.readBytes throws IndexOutOfBoundsException 'EOF trying to read N bytes' when the underlying okio Buffer is exhausted before `length` bytes can be read. It indicates the received frame was truncated or the buffer accounting between HTTP/2 framing and the gRPC message buffer is wrong.
Source
Thrown at okhttp/src/main/java/io/grpc/okhttp/OkHttpReadableBuffer.java:66
}
private void fakeEofExceptionMethod() throws EOFException {}
@Override
public void skipBytes(int length) {
try {
buffer.skip(length);
} catch (EOFException e) {
throw new IndexOutOfBoundsException(e.getMessage());
}
}
@Override
public void readBytes(byte[] dest, int destOffset, int length) {
while (length > 0) {
int bytesRead = buffer.read(dest, destOffset, length);
if (bytesRead == -1) {
throw new IndexOutOfBoundsException("EOF trying to read " + length + " bytes");
}
length -= bytesRead;
destOffset += bytesRead;
}
}
@Override
public void readBytes(OutputStream dest, int length) throws IOException {
buffer.writeTo(dest, length);
}
@Override
public ReadableBuffer readBytes(int length) {
okio.Buffer buf = new okio.Buffer();
buf.write(buffer, length);
return new OkHttpReadableBuffer(buf);
}
View on GitHub (pinned to 64daddc1f3)
Solutions
- Retry the RPC; a truncated frame usually means the stream failed mid-transfer
- Check network path for proxies/load balancers that truncate large responses (raise proxy limits, disable buffering)
- Upgrade grpc-okhttp / grpc-netty to rule out known framing bugs
- Enable gRPC/okhttp logging and HTTP/2 frame traces to find where the truncation occurs
Example fix
// before // receiving side trusts message length; peer sends shorter frame -> EOF // after // enforce max inbound message size so oversized/corrupt lengths fail fast: OkHttpChannelBuilder.forAddress(host, port).maxInboundMessageSize(4 * 1024 * 1024).build();
Defensive patterns
Strategy: retry
Validate before calling
// enforce sane inbound size before sending large messages
if (msg.size() > maxInboundMessageSize) throw new IllegalArgumentException("message too large"); Try / catch
try { stub.unaryCall(req); } catch (StatusRuntimeException e) { if (e.getStatus().getCode() == Status.Code.INTERNAL || e.getStatus().getCode() == Status.Code.UNAVAILABLE) { retryWithBackoff(); } throw e; } Prevention
- Keep maxInboundMessageSize consistent on client and server
- Avoid proxies that truncate large streamed bodies
- Pin tested grpc-okhttp versions and watch for framing bugfixes
When it happens
Trigger: Reading a serialized gRPC message from a transport buffer where fewer bytes arrived than the message header declared (truncated frame, stream reset mid-message, or corrupt length prefix).
Common situations: Network interruption cutting a DATA frame short, HTTP/2 flow-control or framing bugs, proxies that truncate long responses, mismatched message length fields from a faulty peer.
Related errors
- TLS ALPN negotiation failed with protocols: ${protocols}
- Invalid frame length ${dataLength}
- Unable to load OkHttpChannelProvider
- Malformed status code
- closed
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/837098e94d6c37ea.
Report an issue: GitHub.