{"record":{"id":"5410f18456f53703","repo":"t8y2/dbx","slug":"zookeeper-digest-md5-negotiation-exceeded-d-round","errorCode":null,"errorMessage":"ZooKeeper DIGEST-MD5 negotiation exceeded %d rounds","messagePattern":"ZooKeeper DIGEST-MD5 negotiation exceeded (.+?) rounds","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/zookeeper/sasl.go","lineNumber":116,"sourceCode":"\t\t}\n\t\tif saslClient.Complete() {\n\t\t\tif len(challenge) != 0 {\n\t\t\t\treturn errors.New(\"ZooKeeper sent an unexpected token after DIGEST-MD5 completion\")\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\ttoken, err = saslClient.Step(challenge)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"continue ZooKeeper DIGEST-MD5 negotiation at round %d: %w\", round+1, err)\n\t\t}\n\t\tif saslClient.Complete() {\n\t\t\tif len(token) != 0 {\n\t\t\t\treturn errors.New(\"ZooKeeper DIGEST-MD5 completed with an unexpected client token\")\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn fmt.Errorf(\"ZooKeeper DIGEST-MD5 negotiation exceeded %d rounds\", zooKeeperSASLMaxRounds)\n}\n\nfunc zooKeeperSASLRound(connection net.Conn, xid int32, token []byte) ([]byte, error) {\n\tpayload := make([]byte, 12+len(token))\n\tbinary.BigEndian.PutUint32(payload[0:4], uint32(xid))\n\tbinary.BigEndian.PutUint32(payload[4:8], uint32(zooKeeperSASLOpcode))\n\tbinary.BigEndian.PutUint32(payload[8:12], uint32(len(token)))\n\tcopy(payload[12:], token)\n\tif err := writeZooKeeperFrame(connection, payload); err != nil {\n\t\treturn nil, err\n\t}\n\tresponse, err := readZooKeeperFrame(connection)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(response) < 20 {\n\t\treturn nil, errors.New(\"ZooKeeper SASL response is truncated\")\n\t}","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/zookeeper/sasl.go#L98-L134","documentation":"The DIGEST-MD5 handshake is bounded by zooKeeperSASLMaxRounds rounds. If the loop completes all rounds without saslClient.Complete() returning true, the negotiation is deemed stuck and this error is returned. It guards against infinite or divergent challenge/response loops with a misbehaving peer.","triggerScenarios":"authenticateSASLDigest → negotiateSASLDigest runs zooKeeperSASLMaxRounds iterations; each round yields a challenge and a token, but saslClient.Complete() never becomes true, so the function exits via the final return.","commonSituations":"Server keeps issuing challenges (unusual/qop mismatch), a man-in-the-middle or proxy replaying challenges, or a SASL client/step-count mismatch causing the exchange never to converge.","solutions":["Capture a wire log of the SASL exchange to see whether the server keeps re-challenging.","Verify both sides agree on DIGEST-MD5 qop and that no proxy is mangling/replaying challenges.","Check credentials are correct; some flows fail to converge with wrong secrets.","Increase scrutiny of server version — nonstandard ZooKeeper SASL implementations may require more rounds than zooKeeperSASLMaxRounds allows."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := negotiateSASLDigest(conn, creds); err != nil {\n\tif strings.Contains(err.Error(), \"exceeded\") {\n\t\tlog.Printf(\"SASL did not converge in %d rounds; check peer/proxy behavior\", maxRounds)\n\t}\n\treturn err\n}","preventionTips":["Log each SASL round's challenge length to detect endless re-challenging.","Ensure no middlebox replays or reorders challenge frames.","Use a standards-compliant SASL client implementation for DIGEST-MD5."],"tags":["sasl","authentication","zookeeper","protocol"],"backgroundTag":"sasl-handshake-failed","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}