t8y2/dbx · error
decode ZooKeeper session password: %w
Error message
decode ZooKeeper session password: %w
What it means
The final field of the ZooKeeper connect response is the session password as a length-prefixed byte slice; if decoder.bytes() cannot read it, this wrapped error is returned. It is the last decode step of the handshake, so a failure here means the frame was almost complete but truncated or malformed at the tail.
Source
Thrown at agents/drivers/argo-go/zookeeper_protocol.go:174
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
}
if milliseconds > math.MaxInt32 {
return math.MaxInt32
}
return int32(milliseconds)
}
View on GitHub (pinned to c0390bff16)
Solutions
- Verify the password field is present with a correct 4-byte length prefix followed by exactly that many bytes
- Fix the test fake/handler to append the password bytes after the session ID
- Inspect intermediaries for frame truncation and compare against a direct connection capture
- Confirm ZooKeeper client/server wire-format compatibility
Example fix
// before (fake server) resp := append(version, timeout...) resp = append(resp, sessionID...) // password omitted // after resp = append(resp, sessionID...) resp = append(resp, int32(len(passwd))) resp = append(resp, passwd...)
Defensive patterns
Strategy: type-guard
Validate before calling
func frameHasPassword(decoder *zooKeeperDecoder) bool {
return decoder.remaining() >= 4 // at least the password length prefix remains
} Try / catch
client, err := newProtocolZooKeeperClient(conn, timeout)
if err != nil {
if strings.Contains(err.Error(), "decode ZooKeeper session password") {
log.Error("connect response truncated at password field; fix frame length or length prefix")
}
return err
} Prevention
- Always append the length-prefixed password when constructing fake responses
- Validate total frame length against the ConnectResponse layout before sending in tests
- Diff fake responses byte-for-byte against real server captures
When it happens
Trigger: newProtocolZooKeeperClient decoding a response where the password length prefix or its bytes run past the end of the frame — truncated tail, wrong length prefix, or a fake response built without the password field.
Common situations: Manually crafted test responses missing the password bytes; frame off-by-one in a custom proxy; server implementations or versions emitting a different trailing layout.
Related errors
- decode ZooKeeper protocol version: %w
- decode ZooKeeper session timeout: %w
- decode ZooKeeper session ID: %w
- decode ZooKeeper SASL round %d: %w
- ZooKeeper sent an unexpected token after GSSAPI completion
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/076afed6247e6910.
Report an issue: GitHub.