{"record":{"id":"4a9f2e6bb4e329c9","repo":"jhy/jsoup","slug":"underlying-input-stream-returned-zero-bytes","errorCode":null,"errorMessage":"Underlying input stream returned zero bytes","messagePattern":"Underlying input stream returned zero bytes","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/jsoup/internal/SimpleStreamReader.java","lineNumber":83,"sourceCode":"    }\n\n    private boolean hasAvailableBytes() {\n        try {\n            return in.available() > 0;\n        } catch (IOException e) {\n            return false; // available() is advisory; a real read can still consume buffered bytes or reach EOF\n        }\n    }\n\n    private int bufferUp() throws IOException {\n        assert byteBuf != null; // already validated ^\n        byteBuf.compact();\n        try {\n            int pos = byteBuf.position();\n            int remaining = (byteBuf.limit() - pos);\n            int read = in.read(byteBuf.array(), byteBuf.arrayOffset() + pos, remaining);\n            if (read < 0) return read;\n            if (read == 0) throw new IOException(\"Underlying input stream returned zero bytes\");\n            byteBuf.position(pos + read);\n        } finally {\n            byteBuf.flip();\n        }\n        return byteBuf.remaining();\n    }\n\n    @Override\n    public void close() throws IOException {\n        if (byteBuf == null) return;\n        BufferPool.release(byteBuf.array());\n        byteBuf = null;\n        in.close();\n    }\n}\n","sourceCodeStart":65,"sourceCodeEnd":99,"githubUrl":"https://github.com/jhy/jsoup/blob/9851ac5d9c576c6888910b5a51a2362bbc978959/src/main/java/org/jsoup/internal/SimpleStreamReader.java#L65-L99","documentation":"jsoup's internal SimpleStreamReader wraps the underlying InputStream to fill its byte buffer. The JDK contract for InputStream.read(byte[],int,int) is to return -1 at EOF and >0 on success; a 0 return means the stream is misbehaving (returning nothing without blocking or EOF), so jsoup throws an IOException to fail fast rather than loop forever.","triggerScenarios":"The underlying InputStream supplied to a Parser/DataUtil returns 0 from read() instead of blocking, returning data, or -1. This typically happens with a custom/buggy InputStream implementation, a zero-length non-blocking stream, or a stream whose available() logic drives a bad read loop.","commonSituations":"Developers passing hand-rolled InputStreams, wrappers around non-blocking sockets/channels, or test mock streams that return 0 for empty buffers; also streaming from pipes or custom servlet input streams that misreport EOF.","solutions":["Fix the underlying InputStream so read() returns -1 at EOF and blocks until at least one byte is available, never returning 0 with a positive length argument","Check that the buffer length passed to read() is > 0; a zero-length read array can legally return 0","Wrap non-blocking sources (NIO channels) in a blocking adapter or load the data into a byte[] and use Jsoup.parse(String) instead","If the stream is from an HTTP client, verify the connection is still open and the stream was not consumed/already closed"],"exampleFix":"// before (bad custom stream)\nInputStream in = new ByteArrayInputStream(new byte[0]) {\n    public int read(byte[] b, int off, int len) { return 0; }\n};\n// after\nInputStream in = new ByteArrayInputStream(new byte[0]); // read() returns -1 at EOF","handlingStrategy":"try-catch","validationCode":"// ensure the stream is sane before parsing\nbyte[] data = in.readAllBytes(); // throws if the stream misbehaves; ByteArrayInputStream.read() never returns 0\nDocument doc = Jsoup.parse(new ByteArrayInputStream(data), null, baseUri);","typeGuard":"boolean isSaneStream(InputStream in) { return !(in instanceof NullLikeStream); } // use JDK streams or well-tested wrappers","tryCatchPattern":"try { Document doc = Jsoup.parse(in, charset, baseUri); } catch (IOException e) { if (e.getMessage().contains(\"zero bytes\")) { /* replace stream with byte[] source or rethrow */ } throw e; }","preventionTips":["Prefer loading bytes first (readAllBytes) and parsing from byte[]/String for non-blocking sources","Never implement InputStream.read to return 0; return -1 at EOF","Use blocking adapters for NIO channels","Test custom streams with a reader loop that asserts read() != 0"],"tags":["io","input-stream","streaming"],"backgroundTag":"invalid-argument-value","analyzedSha":"9851ac5d9c576c6888910b5a51a2362bbc978959","analyzedAt":"2026-09-08T15:22:04.931Z","contentChangedAt":"2026-09-08T15:22:04.931Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}