jenkinsci/jenkins · critical · IOException

expected to see initial zero byte; perhaps you are connectin

Error message

expected to see initial zero byte; perhaps you are connecting to an old server which does not support -http?

What it means

Thrown during the plain (non-Remoting) CLI protocol over HTTP after FullDuplexHttpStream is established. The protocol expects the server to send an initial zero byte as a handshake; a non-zero first byte means the endpoint did not speak the '-http' plain CLI protocol. Typically indicates an old Jenkins server or a non-Jenkins endpoint.

Source

Thrown at cli/src/main/java/hudson/cli/CLI.java:429

            return connection.exit();
        }
    }

    private static int plainHttpConnection(String url, List<String> args, CLIConnectionFactory factory)
            throws GeneralSecurityException, IOException, InterruptedException {
        LOGGER.log(FINE, "Trying to connect to {0} via plain protocol over HTTP", url);
        if (factory.noCertificateCheck) {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[] {new NoCheckTrustManager()}, new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier((s, sslSession) -> true);
        }
        FullDuplexHttpStream streams = new FullDuplexHttpStream(new URL(url), "cli?remoting=false", factory.authorization);
        try (ClientSideImpl connection = new ClientSideImpl(new PlainCLIProtocol.FramedOutput(streams.getOutputStream()))) {
            connection.start(args);
            InputStream is = streams.getInputStream();
            if (is.read() != 0) { // cf. FullDuplexHttpService
                throw new IOException("expected to see initial zero byte; perhaps you are connecting to an old server which does not support -http?");
            }
            new PlainCLIProtocol.FramedReader(connection, is).start();
            new Thread("ping") { // JENKINS-46659
                @Override
                public void run() {
                    try {
                        Thread.sleep(PING_INTERVAL);
                        while (!connection.complete) {
                            LOGGER.fine("sending ping");
                            connection.sendEncoding(Charset.defaultCharset().name()); // no-op at this point
                            Thread.sleep(PING_INTERVAL);
                        }
                    } catch (IOException | InterruptedException x) {
                        LOGGER.log(Level.WARNING, null, x);
                    }
                }

            }.start();

View on GitHub (pinned to 2e228ff40b)

Solutions

  1. Ensure the Jenkins controller is on a recent supported version that serves the plain CLI protocol over HTTP (the /cli?remoting=false endpoint).
  2. Match the CLI jar version to the controller version; download the CLI jar from the target controller's /jnlpJars/ path.
  3. If a proxy is in the path, bypass it for the CLI endpoint or reconfigure it to stream the response untouched.

Example fix

# before: CLI jar newer than the controller's protocol support
java -jar cli.jar -s http://old-jenkins help
# after: pull the matching CLI jar from the controller
wget http://old-jenkins/jnlpJars/jenkins-cli.jar && java -jar jenkins-cli.jar -s http://old-jenkins help
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the controller serves the plain CLI protocol before connecting
HttpURLConnection c = (HttpURLConnection) new URL(baseUrl + "/cli?remoting=false").openConnection();
c.setRequestMethod("POST");
if (c.getResponseCode() != 200) {
    throw new IOException("Controller does not support plain CLI over HTTP");
}

Try / catch

try {
    // plain protocol handshake
} catch (IOException e) {
    if (e.getMessage().contains("initial zero byte")) {
        // fall back to SSH or Remoting transport
    } else throw e;
}

Prevention

When it happens

Trigger: CLI is run against a Jenkins version too old to support the plain CLI protocol over HTTP (pre-2.x or a version where the /cli endpoint was unavailable), or a proxy/firewall that injects a byte before the real response stream.

Common situations: Upgrading only the CLI jar while the controller stays on a very old version; a man-in-the-middle/proxy rewriting the response body; load balancer returning an error HTML page in place of the duplex stream.

Related errors


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