{"record":{"id":"40b0d9320bca4ae3","repo":"t8y2/dbx","slug":"zookeeper-sasl-round-d-w-40b0d9","errorCode":null,"errorMessage":"ZooKeeper SASL round %d: %w","messagePattern":"ZooKeeper SASL round (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/zookeeper/sasl.go","lineNumber":97,"sourceCode":"}\n\nfunc negotiateSASLDigest(connection net.Conn, timeout time.Duration, saslClient saslClient) error {\n\tif timeout <= 0 {\n\t\ttimeout = defaultConnectionTimeout\n\t}\n\tif err := connection.SetDeadline(time.Now().Add(timeout)); err != nil {\n\t\treturn err\n\t}\n\tdefer connection.SetDeadline(time.Time{})\n\n\ttoken, err := saslClient.Start()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"start ZooKeeper DIGEST-MD5 negotiation: %w\", err)\n\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}","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/zookeeper/sasl.go#L79-L115","documentation":"During the DIGEST-MD5 challenge/response loop, each call to zooKeeperSASLRound (a network round trip carrying the SASL exchange) that fails is wrapped as \"ZooKeeper SASL round %d: %w\". This means the protocol exchange with the server broke at a specific round — I/O error, malformed server reply, frame error, or a server error code. The round number (1-based) is included to locate the failure.","triggerScenarios":"negotiateSASLDigest, invoked by authenticateSASLDigest, calls zooKeeperSASLRound for round N and it returns an error (connection drop, truncated frame, wrong xid, invalid token length, or nonzero server error code).","commonSituations":"Server closes the connection mid-handshake after rejecting credentials, a proxy/load balancer drops the connection, network instability, or the server does not actually support SASL and replies with an error code.","solutions":["Read the wrapped inner error and the round number to see whether it's I/O, protocol, or a server error code.","Verify the server actually has SASL enabled (zoo.cfg: authProvider + sessionRequireClientSASLAuth as appropriate).","Check network path (proxies, LB idle timeouts) between client and ZooKeeper on port 2181.","Retry authenticateSASLDigest if the cause was a transient connection drop.","Confirm credentials match the server's digest store; repeated rejections surface here as server error codes."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if conn == nil { return errors.New(\"connection required before SASL negotiation\") }","typeGuard":null,"tryCatchPattern":"err := negotiateSASLDigest(conn, creds)\nif err != nil {\n\tif errors.Is(err, io.ErrUnexpectedEOF) || isNetTimeout(err) {\n\t\tconn.Close(); conn = dial(); err = negotiateSASLDigest(conn, creds)\n\t}\n\tif err != nil { return fmt.Errorf(\"sasl handshake: %w\", err) }\n}","preventionTips":["Set reasonable connection deadlines and dial fresh connections on handshake failure.","Confirm server SASL config (authProvider, JAAS) before deploying clients.","Avoid proxies that idle-timeout mid-handshake, or raise their timeouts."],"tags":["sasl","network","zookeeper","authentication"],"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"}