jenkinsci/jenkins · error · ProtocolException

unknown operation #{}

Error message

unknown operation #{}

What it means

Thrown by EitherSide.handle when the opcode byte is non-negative but greater than or equal to Op.values().length. This means the peer sent an operation code the local side does not know about, indicating a version mismatch (newer client talking to older server, or vice versa).

Source

Thrown at cli/src/main/java/hudson/cli/PlainCLIProtocol.java:192

    }

    abstract static class EitherSide implements Closeable {

        private final Output out;

        protected EitherSide(Output out) {
            this.out = out;
        }

        protected abstract void handleClose();

        final void handle(DataInputStream dis) throws IOException {
            byte b = dis.readByte();
            if (b < 0) { // i.e., >127
                throw new IOException("corrupt stream: negative operation code");
            }
            if (b >= Op.values().length) {
                throw new ProtocolException("unknown operation #" + b);
            }
            Op op = Op.values()[b];
            LOGGER.finest(() -> "handling frame with " + op);
            if (!handle(op, dis)) {
                throw new ProtocolException("unhandled: " + op);
            }
        }

        protected abstract boolean handle(Op op, DataInputStream dis) throws IOException;

        protected final synchronized void send(Op op) throws IOException {
            send(op, new byte[0], 0, 0);
        }

        protected final synchronized void send(Op op, int v) throws IOException {
            ByteArrayOutputStream baos = new ByteArrayOutputStream(4);
            new DataOutputStream(baos).writeInt(v);
            send(op, baos.toByteArray());

View on GitHub (pinned to 2e228ff40b)

Solutions

  1. Align CLI jar and controller versions; fetch the CLI jar from the target controller's /jnlpJars/jenkins-cli.jar.
  2. Upgrade both ends to the same Jenkins LTS line.
  3. If you cannot upgrade, restrict CLI usage to operations supported by the older protocol version.

Example fix

# before: mismatched versions
java -jar /old/cli.jar -s http://new-jenkins ... 
# after
curl -sO http://new-jenkins/jnlpJars/jenkins-cli.jar && java -jar jenkins-cli.jar -s http://new-jenkins ...
Defensive patterns

Strategy: validation

Validate before calling

// Before sending, ensure the local Op set is at least as large as the peer's
// (compare CLI/protocol versions). If unknown opcodes are possible, negotiate a version.
int maxOp = Arrays.stream(Op.values()).mapToInt(Enum::ordinal).max().getAsInt();
if (peerMaxOp > maxOp) {
    throw new IOException("Peer protocol version is newer; upgrade the CLI jar");
}

Try / catch

try {
    side.handle(dis);
} catch (ProtocolException e) {
    if (e.getMessage().startsWith("unknown operation")) {
        // reconnect using a CLI jar version matching the controller
    } else throw e;
}

Prevention

When it happens

Trigger: One side of the PlainCLIProtocol connection uses an Op enum that was extended in a newer Jenkins/CLI version while the other side runs an older build with fewer Op values.

Common situations: CLI jar downloaded from a newer Jenkins used against an older controller; controller upgraded but a bundled/older CLI jar is still in use.

Related errors


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