Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java readNBytes: reject a truncated payload after the header

Last updated: 5 Oct 20264 min read
tutorial
IntermediateBy AITrove Editorial

InputStream.readNBytes(n) returns at most n bytes; an early end of stream can make the returned array shorter.

Validate the declared length

A wire header is untrusted input. Check its sign and a protocol maximum before asking readNBytes to allocate. Then compare the returned array length with the declared size. End of stream after the header is a truncated frame, not an empty field or a successful partial message.

This example uses a one-byte length prefix to isolate the rule. Larger frames should use a bounded header reader and a fixed-size chunk loop when allocation pressure matters.

Decide who owns the bytes

The returned array belongs to the caller. Do not expose it as mutable shared application state without a copy or a clear ownership transfer. A readFully call provides a different API contract but still requires a maximum before buffer allocation.

Working program

Java
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

public class ShipmentFrameReader {
    static String readShipment(InputStream wire) throws IOException {
        int declared = wire.read();
        if (declared < 0) throw new EOFException("missing length");
        if (declared > 64) throw new IOException("frame too large");
        byte[] payload = wire.readNBytes(declared);
        if (payload.length != declared) throw new EOFException("truncated payload");
        return new String(payload, StandardCharsets.US_ASCII);
    }

    public static void main(String[] args) throws IOException {
        System.out.println(readShipment(new ByteArrayInputStream(new byte[] {4, 'A', 'B', '4', '7'})));
        try { readShipment(new ByteArrayInputStream(new byte[] {5, 'A', 'B'})); }
        catch (EOFException truncated) { System.out.println(truncated.getMessage()); }
    }
}

Output

Output
AB47
truncated payload

Cost and ownership

readNBytes allocates up to the requested payload size, so the cap is a memory policy, not an optional validation. Time is O(n) for n bytes received. This example targets Java 11 or later; a streaming decoder can avoid retaining the whole frame.

Common Mistakes

  • Do not infer that readNBytes always returns the requested count.
  • Do not let a header dictate an unbounded allocation.
  • Do not treat an incomplete payload as a valid message.

Read next

Java InputStream short reads: assemble a complete record header, datainputstream readfully eof, Java byte streams: partial reads and bounded copying, Java UTF-8 decoding: reject malformed bytes before parsing.

java
io
readnbytes-truncated-frame
Storage details