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
- Align CLI jar and controller versions; fetch the CLI jar from the target controller's /jnlpJars/jenkins-cli.jar.
- Upgrade both ends to the same Jenkins LTS line.
- 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
- Pin the CLI jar version to the controller version.
- Re-download the CLI jar from the controller after a Jenkins upgrade.
- Standardize on one Jenkins LTS line across servers you manage.
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
- unhandled: {}
- expected to see initial zero byte; perhaps you are connectin
- corrupt stream: negative operation code
- corrupt stream: negative frame length
- There's no Jenkins running at {}
AI-assisted analysis of jenkinsci/jenkins@2e228ff40b (2026-08-14).
Data as JSON: /api/errors/79465306634d9d9f.
Report an issue: GitHub.