Tencent/tinker · error · NullPointerException
dst == null
Error message
dst == null
What it means
Argument-validation twin of the 'in == null' check in Streams.readFully: when the destination byte[] is null (and byteCount > 0), the method throws NullPointerException("dst == null") before any read is attempted. It exists so callers get a clear programming-error message instead of an opaque NPE deep in the copy loop.
Source
Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/Streams.java:78
public static void readFully(InputStream in, byte[] dst) throws IOException {
readFully(in, dst, 0, dst.length);
}
/**
* Reads exactly 'byteCount' bytes from 'in' (into 'dst' at offset 'offset'), and throws
* EOFException if insufficient bytes are available.
*
* Used to implement {@link java.io.DataInputStream#readFully(byte[], int, int)}.
*/
public static void readFully(InputStream in, byte[] dst, int offset, int byteCount) throws IOException {
if (byteCount == 0) {
return;
}
if (in == null) {
throw new NullPointerException("in == null");
}
if (dst == null) {
throw new NullPointerException("dst == null");
}
Arrays.checkOffsetAndCount(dst.length, offset, byteCount);
while (byteCount > 0) {
int bytesRead = in.read(dst, offset, byteCount);
if (bytesRead < 0) {
throw new EOFException();
}
offset += bytesRead;
byteCount -= bytesRead;
}
}
/**
* Returns a byte[] containing the remainder of 'in', closing it when done.
*/
public static byte[] readFully(InputStream in) throws IOException {
try {
return readFullyNoClose(in);
} finally {View on GitHub (pinned to 1b7ea02c23)
Solutions
- Allocate the destination buffer before calling readFully, and handle the zero-length case via the byteCount == 0 early return rather than by nulling the array.
- Keep parse state immutable per parse pass (local buffers, not shared nulled fields) to avoid races.
- Add Objects.requireNonNull(dst) at your own API boundary to fail with your context, not the library's.
Example fix
// before: null buffer for the empty case
byte[] extra = extraLen > 0 ? new byte[extraLen] : null;
Streams.readFully(in, extra, 0, extraLen); // NPE when extraLen == 0 path misused
// after: always allocate; rely on byteCount == 0 early return
byte[] extra = new byte[Math.max(0, extraLen)];
if (extraLen > 0) {
Streams.readFully(in, extra, 0, extraLen);
} Defensive patterns
Strategy: validation
Validate before calling
// Always allocate a destination buffer (zero-length handled by byteCount==0 early return) byte[] dst = new byte[Math.max(0, count)]; Streams.readFully(in, dst, 0, count);
Prevention
- Allocate buffers unconditionally; express 'nothing to read' via count==0, not a null array.
- Use local (per-call) buffers instead of shared nulled fields to avoid races.
- Require non-null buffers in your own APIs so the failure surfaces with your context.
When it happens
Trigger: Streams.readFully called with a null destination buffer — e.g. a buffer allocated conditionally (size read from a header) where the allocation was skipped or moved, or a field buffer nulled by a concurrent close/reset path.
Common situations: Header-driven allocation (`byte[] buf = len > 0 ? new byte[len] : null`) then readFully invoked unconditionally; buffer pooling that recycles arrays to null; race where one thread clears state while another still parses.
Related errors
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/b14b206f1f27bb91.
Report an issue: GitHub.