{"record":{"id":"3d844ff04dafd7f4","repo":"t8y2/dbx","slug":"zookeeper-sasl-token-is-truncated","errorCode":null,"errorMessage":"ZooKeeper SASL token is truncated","messagePattern":"ZooKeeper SASL token is truncated","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/zookeeper/sasl.go","lineNumber":144,"sourceCode":"\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}\n\tresponseXID := int32(binary.BigEndian.Uint32(response[4:8]))\n\tif responseXID != xid {\n\t\treturn nil, fmt.Errorf(\"ZooKeeper SASL response xid %d does not match request xid %d\", responseXID, xid)\n\t}\n\terrorCode := int32(binary.BigEndian.Uint32(response[16:20]))\n\tif errorCode != 0 {\n\t\treturn nil, fmt.Errorf(\"ZooKeeper SASL server returned error %d\", errorCode)\n\t}\n\tif len(response) < 24 {\n\t\treturn nil, errors.New(\"ZooKeeper SASL token is truncated\")\n\t}\n\ttokenLength := int(int32(binary.BigEndian.Uint32(response[20:24])))\n\tif tokenLength < 0 || tokenLength > zooKeeperMaximumFrameLen || 24+tokenLength > len(response) {\n\t\treturn nil, fmt.Errorf(\"ZooKeeper SASL token length %d is invalid\", tokenLength)\n\t}\n\treturn append([]byte(nil), response[24:24+tokenLength]...), nil\n}\n\nfunc readZooKeeperFrame(reader io.Reader) ([]byte, error) {\n\theader := make([]byte, 4)\n\tif _, err := io.ReadFull(reader, header); err != nil {\n\t\treturn nil, err\n\t}\n\tlength := int(int32(binary.BigEndian.Uint32(header)))\n\tif length < 0 || length > zooKeeperMaximumFrameLen {\n\t\treturn nil, fmt.Errorf(\"ZooKeeper frame length %d is invalid\", length)\n\t}\n\tpayload := make([]byte, length+4)","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/zookeeper/sasl.go#L126-L162","documentation":"After a successful (error code 0) SASL response header, the 4-byte token length field is expected at bytes 20-24. If the frame ends there, the response carries no token body, which the protocol requires, so the driver rejects it as truncated.","triggerScenarios":"The SASL response frame is at least 20 bytes (header OK, errorCode 0) but shorter than 24 bytes, so the token length field itself is missing.","commonSituations":"Server bug sending an empty SASL completion, a man-in-the-middle/proxy stripping payload bytes, or a custom server build with malformed SASL replies.","solutions":["Capture the raw frame and compare with a standard ZooKeeper SASL handshake","Check for intermediaries (proxies, TLS terminators) mangling the stream","Retry authentication; if reproducible, verify the server's SASL provider emits a token on success","Update the ZooKeeper server or driver versions"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"token, err := zooKeeperSASLRound(conn, xid, challenge)\nif err != nil {\n    if strings.Contains(err.Error(), \"token is truncated\") {\n        // capture/inspect raw frame, reconnect, and report server-side issue\n    }\n    return err\n}","preventionTips":["Keep client and server on compatible ZooKeeper versions","Bypass or correctly configure TLS-terminating proxies","Add packet-level logging when enabling SASL for the first time","Retry once on truncation before escalating to auth failure"],"tags":["zookeeper","sasl","protocol","truncated"],"backgroundTag":"truncated-response","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"}