{"record":{"id":"cb4177d319d461c0","repo":"jenkinsci/jenkins","slug":"datainputstream-unexpectedly-returned-negative-int","errorCode":null,"errorMessage":"DataInputStream unexpectedly returned negative integer","messagePattern":"DataInputStream unexpectedly returned negative integer","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/hudson/cli/Connection.java","lineNumber":142,"sourceCode":"\n    public void writeKey(Key key) throws IOException {\n        writeUTF(Base64.getEncoder().encodeToString(key.getEncoded()));\n    }\n\n    public X509EncodedKeySpec readKey() throws IOException {\n        byte[] otherHalf = Base64.getDecoder().decode(readUTF()); // for historical reasons, we don't use readByteArray()\n        return new X509EncodedKeySpec(otherHalf);\n    }\n\n    public void writeByteArray(byte[] data) throws IOException {\n        dout.writeInt(data.length);\n        dout.write(data);\n    }\n\n    public byte[] readByteArray() throws IOException {\n        int bufSize = din.readInt();\n        if (bufSize < 0) {\n            throw new IOException(\"DataInputStream unexpectedly returned negative integer\");\n        }\n        byte[] buf = new byte[bufSize];\n        din.readFully(buf);\n        return buf;\n    }\n\n    /**\n     * Performs a Diffie-Hellman key exchange and produce a common secret between two ends of the connection.\n     *\n     * <p>\n     * DH is also useful as a coin-toss algorithm. Two parties get the same random number without trusting\n     * each other.\n     */\n    public KeyAgreement diffieHellman(boolean side) throws IOException, GeneralSecurityException {\n        return diffieHellman(side, 512);\n    }\n\n    public KeyAgreement diffieHellman(boolean side, int keySize) throws IOException, GeneralSecurityException {","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/jenkinsci/jenkins/blob/2e228ff40b14dbc8b14ffbc6edf0e4383cf744fc/core/src/main/java/hudson/cli/Connection.java#L124-L160","documentation":"`Connection.readByteArray()` reads a 4-byte length prefix via `din.readInt()` and throws IOException if the resulting `bufSize` is negative. A negative length means the length-prefix bytes were corrupted or deliberately malformed (readInt interprets the high bit as a sign), so allocating `new byte[bufSize]` would be impossible/dangerous. This guards the CLI wire protocol against stream corruption.","triggerScenarios":"Any CLI exchange that calls `readByteArray()` when the peer sent a malformed length prefix, or when the stream has desynchronized (partial/missing bytes), or when a version/protocol mismatch causes the reader to interpret payload data as a length. Also reachable under a man-in-the-middle injecting garbage.","commonSituations":"CLI jar version mismatched with the Jenkins server (different framing), a proxy/load balancer truncating or rewriting the binary stream, network corruption, or an attacker tampering with the connection.","solutions":["Ensure the `jenkins-cli.jar` matches the Jenkins server version exactly (download it from your server's /jnlpJars/jenkins-cli.jar).","Remove or reconfigure any HTTP/TCP proxy in front of Jenkins that may corrupt binary framing; connect directly to test.","If you control both endpoints, verify the write side always emits `writeByteArray` (length-prefixed) matching the read side, and check for stream desynchronization after a prior error."],"exampleFix":"// before: mismatched jar causes negative length prefix\n//   java -jar old-jenkins-cli.jar -s https://jenkins help\n//   -> IOException: DataInputStream unexpectedly returned negative integer\n//\n// after: use the jar shipped by this server\n//   curl -O https://jenkins/jnlpJars/jenkins-cli.jar\n//   java -jar jenkins-cli.jar -s https://jenkins help","handlingStrategy":"try-catch","validationCode":"// Ensure CLI jar and server versions match before connecting:\n//   download the jar from the same server: curl -O $JENKINS/jnlpJars/jenkins-cli.jar","typeGuard":null,"tryCatchPattern":"try {\n    byte[] data = conn.readByteArray();\n} catch (IOException e) {\n    if (e.getMessage().contains(\"negative integer\")) {\n        // protocol/stream desync or version mismatch: reconnect with a fresh,\n        // version-matched CLI jar rather than reusing the broken stream\n        throw new IllegalStateException(\"CLI protocol framing corrupted; \"\n            + \"use the jenkins-cli.jar matching server version and bypass proxies\", e);\n    }\n    throw e;\n}","preventionTips":["Always use the jenkins-cli.jar downloaded from the target Jenkins server.","Avoid HTTP proxies that may alter binary framing for CLI traffic."],"tags":["jenkins","cli","network","protocol","io","security"],"backgroundTag":null,"analyzedSha":"2e228ff40b14dbc8b14ffbc6edf0e4383cf744fc","analyzedAt":"2026-08-14T07:07:15.274Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}