Tencent/tinker · error · NullPointerException
in == null
Error message
in == null
What it means
Streams.readFully(InputStream, byte[], int, int) is a strict replacement for DataInputStream.readFully: it validates its arguments before doing I/O. When byteCount > 0 and the supplied InputStream is null it throws NullPointerException("in == null") — a programming-error guard, not an I/O condition, signaling the caller passed an unopened/failed stream reference.
Source
Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/Streams.java:75
/**
* Fills 'dst' with bytes from 'in', throwing EOFException if insufficient bytes are available.
*/
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 {View on GitHub (pinned to 1b7ea02c23)
Solutions
- Ensure the InputStream is opened (non-null) before constructing readers that consume it; propagate open failures instead of null.
- Change null-returning factory methods to throw the original IOException so the NPE never masks the root cause.
- In tests, pass a real ByteArrayInputStream or a proper mock rather than null.
Example fix
// before: factory returns null on failure, caller passes it on
InputStream open(String name) {
try { return files.get(name); }
catch (IOException e) { return null; }
}
Streams.readFully(open(name), buf, 0, len); // NPE: in == null
// after: propagate the failure
InputStream open(String name) throws IOException {
return files.get(name);
}
try (InputStream in = open(name)) {
Streams.readFully(in, buf, 0, len);
} Defensive patterns
Strategy: validation
Validate before calling
// Ensure a non-null stream before parsing java.util.Objects.requireNonNull(in, "input stream must be opened before reading"); Streams.readFully(in, dst, 0, len);
Prevention
- Open streams in try-with-resources so a failed open propagates instead of yielding null.
- Never write factory methods that return null on error — throw.
- Assert non-null at your API boundary with Objects.requireNonNull for clear failure context.
When it happens
Trigger: Any internal ziputils read path (TinkerZipEntry parsing, TinkerZipFile streaming) invoking Streams.readFully with a null InputStream — typically because a stream-open call earlier returned null after a caught exception instead of propagating.
Common situations: Wrapped stream factories that return null on failure (anti-pattern) feeding zip readers; fields initialized lazily that were never set because of an earlier error; test harnesses constructing parsers with null stream stubs.
Related errors
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/f02c23d6ca708c61.
Report an issue: GitHub.