jenkinsci/jenkins · error · ProtocolException

unhandled: {}

Error message

unhandled: {}

What it means

Thrown by EitherSide.handle when handle(Op, DataInputStream) returns false for a recognized opcode. Each concrete side returns false for an operation it does not expect to handle in that direction (e.g. a server-only Op received by the client, or an unimplemented handler).

Source

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

        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());
        }

        protected final synchronized void send(Op op, byte[] chunk, int off, int len) throws IOException {
            byte[] data = new byte[len + 1];
            data[0] = (byte) op.ordinal();

View on GitHub (pinned to 2e228ff40b)

Solutions

  1. Match the CLI jar version to the controller so both sides implement the same operation set.
  2. If persisting, capture which Op triggered it (enable FINEST logging on hudson.cli.PlainCLIProtocol) and update the older side.
  3. Fall back to SSH or Remoting CLI transport if the plain protocol remains incompatible.
Defensive patterns

Strategy: try-catch

Try / catch

try {
    side.handle(dis);
} catch (ProtocolException e) {
    if (e.getMessage().startsWith("unhandled:")) {
        // log which Op was unhandled, reconnect with matching versions
        LOGGER.log(WARNING, "Unhandled CLI op: {0}", e.getMessage());
    } else throw e;
}

Prevention

When it happens

Trigger: A frame's opcode is within the known range but the receiving side's handle(op) implementation has no branch for it (returns false), typically because the operation is meant for the opposite direction or a newer variant.

Common situations: Protocol asymmetry between client and server builds; a partial upgrade where one side emits an Op the other recognizes but does not process.

Related errors


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