jenkinsci/jenkins · error · IOException

DataInputStream unexpectedly returned negative integer

Error message

DataInputStream unexpectedly returned negative integer

What it means

`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.

Source

Thrown at core/src/main/java/hudson/cli/Connection.java:142

    public void writeKey(Key key) throws IOException {
        writeUTF(Base64.getEncoder().encodeToString(key.getEncoded()));
    }

    public X509EncodedKeySpec readKey() throws IOException {
        byte[] otherHalf = Base64.getDecoder().decode(readUTF()); // for historical reasons, we don't use readByteArray()
        return new X509EncodedKeySpec(otherHalf);
    }

    public void writeByteArray(byte[] data) throws IOException {
        dout.writeInt(data.length);
        dout.write(data);
    }

    public byte[] readByteArray() throws IOException {
        int bufSize = din.readInt();
        if (bufSize < 0) {
            throw new IOException("DataInputStream unexpectedly returned negative integer");
        }
        byte[] buf = new byte[bufSize];
        din.readFully(buf);
        return buf;
    }

    /**
     * Performs a Diffie-Hellman key exchange and produce a common secret between two ends of the connection.
     *
     * <p>
     * DH is also useful as a coin-toss algorithm. Two parties get the same random number without trusting
     * each other.
     */
    public KeyAgreement diffieHellman(boolean side) throws IOException, GeneralSecurityException {
        return diffieHellman(side, 512);
    }

    public KeyAgreement diffieHellman(boolean side, int keySize) throws IOException, GeneralSecurityException {

View on GitHub (pinned to 2e228ff40b)

Solutions

  1. Ensure the `jenkins-cli.jar` matches the Jenkins server version exactly (download it from your server's /jnlpJars/jenkins-cli.jar).
  2. Remove or reconfigure any HTTP/TCP proxy in front of Jenkins that may corrupt binary framing; connect directly to test.
  3. 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.

Example fix

// before: mismatched jar causes negative length prefix
//   java -jar old-jenkins-cli.jar -s https://jenkins help
//   -> IOException: DataInputStream unexpectedly returned negative integer
//
// after: use the jar shipped by this server
//   curl -O https://jenkins/jnlpJars/jenkins-cli.jar
//   java -jar jenkins-cli.jar -s https://jenkins help
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure CLI jar and server versions match before connecting:
//   download the jar from the same server: curl -O $JENKINS/jnlpJars/jenkins-cli.jar

Try / catch

try {
    byte[] data = conn.readByteArray();
} catch (IOException e) {
    if (e.getMessage().contains("negative integer")) {
        // protocol/stream desync or version mismatch: reconnect with a fresh,
        // version-matched CLI jar rather than reusing the broken stream
        throw new IllegalStateException("CLI protocol framing corrupted; "
            + "use the jenkins-cli.jar matching server version and bypass proxies", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jenkinsci/jenkins@2e228ff40b (2026-08-14). Data as JSON: /api/errors/cb4177d319d461c0. Report an issue: GitHub.