{"record":{"id":"5c78d47f259fcd27","repo":"t8y2/dbx","slug":"zookeeper-digest-md5-completed-with-an-unexpected","errorCode":null,"errorMessage":"ZooKeeper DIGEST-MD5 completed with an unexpected client token","messagePattern":"ZooKeeper DIGEST-MD5 completed with an unexpected client token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/zookeeper/sasl.go","lineNumber":111,"sourceCode":"\t}\n\tfor round := 0; round < zooKeeperSASLMaxRounds; round++ {\n\t\tchallenge, err := zooKeeperSASLRound(connection, zooKeeperSASLXIDBase+int32(round), token)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"ZooKeeper SASL round %d: %w\", round+1, err)\n\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 {","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/zookeeper/sasl.go#L93-L129","documentation":"This error is thrown by negotiateSASLDigest when the DIGEST-MD5 SASL client reports Complete() but Step() still returned a non-empty token. A completed DIGEST-MD5 exchange must produce a final empty client token; a non-empty one means the negotiated state machine ended in an inconsistent state.","triggerScenarios":"During ZooKeeper SASL DIGEST-MD5 authentication, a server challenge causes saslClient.Step(challenge) to return a token in the same round where saslClient.Complete() becomes true.","commonSituations":"Mismatched or buggy SASL client implementations, a server sending an extra final challenge with data, or a nonstandard ZooKeeper/quorum peer that appends data to the last DIGEST-MD5 step.","solutions":["Verify the SASL client implementation (e.g. the DIGEST-MD5 library behind saslClient) emits an empty final token on completion","Check that the server is a standard ZooKeeper peer and not a proxy rewriting SASL frames","Compare the server's final challenge against a known-good ZooKeeper SASL handshake capture","Update the driver and SASL dependency versions to aligned, tested releases"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Go: pre-check SASL client behavior with a test harness\nif saslClient.Complete() && len(lastToken) != 0 {\n    // abort handshake before sending; treat client implementation as buggy\n}","typeGuard":null,"tryCatchPattern":"if err := negotiateSASLDigest(conn, saslClient); err != nil {\n    if strings.Contains(err.Error(), \"unexpected client token\") {\n        // log full handshake round trace; fall back to non-SASL auth or fail fast\n        return fmt.Errorf(\"zookeeper SASL implementation mismatch: %w\", err)\n    }\n    return err\n}","preventionTips":["Pin tested versions of the SASL client library and driver together","Add a unit test asserting the final DIGEST-MD5 step returns an empty token","Log each Step() round length during integration testing","Verify against a stock ZooKeeper server before custom deployments"],"tags":["zookeeper","sasl","authentication","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-14T00:17:10.932Z"}