t8y2/dbx · error
ZooKeeper sent an unexpected token after DIGEST-MD5 completi
Error message
ZooKeeper sent an unexpected token after DIGEST-MD5 completion
What it means
negotiateSASLDigest runs the DIGEST-MD5 challenge rounds. Once the SASL client reports Complete(), the protocol expects the server to stop issuing challenges; if another non-empty token arrives after completion, the driver aborts with 'ZooKeeper sent an unexpected token after DIGEST-MD5 completion' because the handshake deviates from RFC 2831/the ZooKeeper SASL exchange.
Source
Thrown at agents/drivers/zookeeper/sasl.go:101
timeout = defaultConnectionTimeout
}
if err := connection.SetDeadline(time.Now().Add(timeout)); err != nil {
return err
}
defer connection.SetDeadline(time.Time{})
token, err := saslClient.Start()
if err != nil {
return fmt.Errorf("start ZooKeeper DIGEST-MD5 negotiation: %w", err)
}
for round := 0; round < zooKeeperSASLMaxRounds; round++ {
challenge, err := zooKeeperSASLRound(connection, zooKeeperSASLXIDBase+int32(round), token)
if err != nil {
return fmt.Errorf("ZooKeeper SASL round %d: %w", round+1, err)
}
if saslClient.Complete() {
if len(challenge) != 0 {
return errors.New("ZooKeeper sent an unexpected token after DIGEST-MD5 completion")
}
return nil
}
token, err = saslClient.Step(challenge)
if err != nil {
return fmt.Errorf("continue ZooKeeper DIGEST-MD5 negotiation at round %d: %w", round+1, err)
}
if saslClient.Complete() {
if len(token) != 0 {
return errors.New("ZooKeeper DIGEST-MD5 completed with an unexpected client token")
}
return nil
}
}
return fmt.Errorf("ZooKeeper DIGEST-MD5 negotiation exceeded %d rounds", zooKeeperSASLMaxRounds)
}
func zooKeeperSASLRound(connection net.Conn, xid int32, token []byte) ([]byte, error) {View on GitHub (pinned to c0390bff16)
Solutions
- Check the ZooKeeper server version and SASL configuration (jaas.conf, digest-md5 settings) for known quirks; upgrade to a version with correct DIGEST-MD5 termination.
- Remove/inspect any proxy between client and ensemble that could inject extra frames.
- Verify the negotiated protection quality (qop) matches on both sides — enable/auth/integrity mismatches can produce extra tokens.
- Capture the handshake (tcpdump/Wireshark on port 2181) and compare token counts against a known-good zkCli SASL session.
Example fix
// server jaas.conf before
// ZooKeeper { org.apache.zookeeper.server.auth.DigestLoginModule required ... user_super="pw"; } // missing/broken entry causes odd SASL exchange
// after
ZooKeeper { org.apache.zookeeper.server.auth.DigestLoginModule required user_svc="correct-pw"; }; Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight SASL sanity: confirm server advertises digest-md5 and credentials exist // zkCli.sh with matching jaas.conf should authenticate without errors before app start
Try / catch
err := authenticateSASLDigest(conn, creds)
if err != nil {
if strings.Contains(err.Error(), "unexpected token after DIGEST-MD5 completion") {
// server/proxy misbehaving; log details and fall back or fail with context
return fmt.Errorf("server sent extra SASL token; check ZooKeeper version/jaas.conf and any proxy: %w", err)
}
return err
} Prevention
- Pin a ZooKeeper server version known to terminate DIGEST-MD5 cleanly
- Avoid proxies/middlewares on the SASL path or test them for frame injection
- Align qop (protection quality) settings on client and server
- Validate SASL login with zkCli against the same ensemble before rollout
When it happens
Trigger: During authenticateSASLDigest, after saslClient.Complete() is true, zooKeeperSASLRound still returns a non-empty challenge — the server sent an extra token after the final client response. Raised in agents/drivers/zookeeper/sasl.go:101.
Common situations: Server-side SASL implementation or version behaving non-strictly (extra post-completion token); a proxy/middleware in front of ZooKeeper injecting an extra frame; server misconfigured with layered security (qop) producing an additional challenge; version mismatch between client library expectations and server SASL behavior.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ZooKeeper DIGEST-MD5 completed with an unexpected client tok
- ZooKeeper DIGEST-MD5 negotiation exceeded %d rounds
- ZooKeeper session closed because SASL authentication is requ
- ZooKeeper sent an unexpected token after GSSAPI completion
- ZooKeeper authentication failed
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/8cd2c75442f54644.
Report an issue: GitHub.