t8y2/dbx · error

decode ZooKeeper session timeout

Error message

decode ZooKeeper session timeout: %w

What it means

The second int32 in the ZooKeeper connect response is the negotiated session timeout; if the decoder cannot extract it, this wrapped error is returned. Like the protocol-version decode, it indicates the response frame is too short or does not follow the ConnectResponse wire format.

Solutions

  1. Verify the response frame length against the expected ConnectResponse layout (negotiated timeout, session id, passwd)
  2. Bypass or reconfigure any TCP proxy/middleware and test a direct connection to ZooKeeper
  3. Fix the test fake to emit a complete, well-formed connect response
  4. Ensure client and server ZooKeeper protocol versions are compatible
Defensive patterns

Strategy: type-guard

Validate before calling

func frameHasTimeout(decoder *zooKeeperDecoder) bool {
    return decoder.remaining() >= 4 // timeout int32 after the version field
}

Try / catch

client, err := newProtocolZooKeeperClient(conn, timeout)
if err != nil {
    if strings.Contains(err.Error(), "decode ZooKeeper session timeout") {
        log.Error("truncated ZK connect response; check proxy MTU/framing")
        return err
    }
    return err
}

Prevention

When it happens

Trigger: newProtocolZooKeeperClient decoding a response whose payload ends after the first int32 (fewer than 8 bytes total in the expected positions) — truncated frames from a broken proxy or a fake/test server emitting malformed data.

Common situations: Custom or middlebox proxies truncating frames; hand-rolled test doubles returning incomplete responses; memory corruption/short writes between server and client; version skew producing a different field order.

Understand the failure class

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/b343639bca186875. Report an issue: GitHub.

Appendix: source

Thrown at agents/drivers/argo-go/zookeeper_protocol.go:167

	request := &zooKeeperEncoder{}
	request.int32(zooKeeperProtocolVersion)
	request.int64(0)
	request.int32(zooKeeperTimeoutMillis(timeout))
	request.int64(0)
	request.bytes(make([]byte, 16))
	if err := client.writeFrame(request.data()); err != nil {
		return nil, fmt.Errorf("send ZooKeeper connect request: %w", err)
	}
	response, err := client.readFrame()
	if err != nil {
		return nil, fmt.Errorf("read ZooKeeper connect response: %w", err)
	}
	decoder := newZooKeeperDecoder(response)
	if _, err := decoder.int32(); err != nil {
		return nil, fmt.Errorf("decode ZooKeeper protocol version: %w", err)
	}
	if _, err := decoder.int32(); err != nil {
		return nil, fmt.Errorf("decode ZooKeeper session timeout: %w", err)
	}
	sessionID, err := decoder.int64()
	if err != nil {
		return nil, fmt.Errorf("decode ZooKeeper session ID: %w", err)
	}
	if _, err := decoder.bytes(); err != nil {
		return nil, fmt.Errorf("decode ZooKeeper session password: %w", err)
	}
	if sessionID == 0 {
		return nil, zk.ErrSessionExpired
	}
	return client, nil
}

func zooKeeperTimeoutMillis(timeout time.Duration) int32 {
	milliseconds := timeout.Milliseconds()
	if milliseconds < 1 {
		return 1

View on GitHub (pinned to c0390bff16)