{"record":{"id":"a8957b8709c14a3a","repo":"t8y2/dbx","slug":"zookeeper-sasl-client-is-nil","errorCode":null,"errorMessage":"ZooKeeper SASL client is nil","messagePattern":"ZooKeeper SASL client is nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/argo-go/zookeeper_protocol.go","lineNumber":195,"sourceCode":"\t\treturn nil, zk.ErrSessionExpired\n\t}\n\treturn client, nil\n}\n\nfunc zooKeeperTimeoutMillis(timeout time.Duration) int32 {\n\tmilliseconds := timeout.Milliseconds()\n\tif milliseconds < 1 {\n\t\treturn 1\n\t}\n\tif milliseconds > math.MaxInt32 {\n\t\treturn math.MaxInt32\n\t}\n\treturn int32(milliseconds)\n}\n\nfunc (client *protocolZooKeeperClient) authenticateSASL(saslClient zooKeeperSASLClient) error {\n\tif saslClient == nil {\n\t\treturn errors.New(\"ZooKeeper SASL client is nil\")\n\t}\n\tdefer saslClient.Dispose()\n\ttoken, err := saslClient.Start()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"start ZooKeeper GSSAPI negotiation: %w\", err)\n\t}\n\tfor round := 0; round < zooKeeperMaxSASLRounds; round++ {\n\t\tresponse, requestErr := client.request(zooKeeperOpSASL, func(encoder *zooKeeperEncoder) {\n\t\t\tif token == nil {\n\t\t\t\tencoder.bytes([]byte{})\n\t\t\t\treturn\n\t\t\t}\n\t\t\tencoder.bytes(token)\n\t\t})\n\t\tif requestErr != nil {\n\t\t\treturn fmt.Errorf(\"ZooKeeper SASL round %d: %w\", round+1, requestErr)\n\t\t}\n\t\tdecoder := newZooKeeperDecoder(response)","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/argo-go/zookeeper_protocol.go#L177-L213","documentation":"protocolZooKeeperClient.authenticateSASL requires a non-nil zooKeeperSASLClient; passing nil returns this error before Dispose or any negotiation begins. The SASL client drives the GSSAPI challenge/response rounds, so without it Kerberos authentication cannot proceed.","triggerScenarios":"Calling authenticateSASL(nil), or passing a SASL client variable that was never initialized because credential loading (keytab/ccache) failed silently upstream.","commonSituations":"Lazy initialization of the GSSAPI client that was skipped on an error path; test doubles not wired in; factory functions returning nil instead of an error when Kerberos credentials are missing.","solutions":["Ensure the GSSAPI/SASL client is constructed from valid Kerberos credentials before calling authenticateSASL","Fix the factory that produced a nil client without an error","Skip authenticateSASL only when the server does not require SASL — never pass nil to satisfy the signature","Add an assertion/check at the call site for a nil SASL client"],"exampleFix":"// before\nsasl := buildSASLClient(cfg) // may return nil\nerr := zkClient.authenticateSASL(sasl)\n// after\nsasl := buildSASLClient(cfg)\nif sasl == nil {\n    return errors.New(\"failed to build ZooKeeper SASL client from Kerberos credentials\")\n}\nerr := zkClient.authenticateSASL(sasl)","handlingStrategy":"validation","validationCode":"if saslClient == nil {\n    return errors.New(\"SASL client not initialized; check Kerberos credential loading\")\n}","typeGuard":"func hasSASLClient(c zooKeeperSASLClient) bool { return c != nil }","tryCatchPattern":"if err := zkClient.authenticateSASL(sasl); err != nil {\n    if strings.Contains(err.Error(), \"SASL client is nil\") {\n        // rebuild the SASL client from credentials and retry\n    }\n}","preventionTips":["Build the GSSAPI client before authentication and propagate its errors","Never pass nil to satisfy the API — skip SASL only when the server permits it","Add startup checks that Kerberos credentials produced a usable SASL client"],"tags":["zookeeper","sasl","kerberos","nil-check","go"],"backgroundTag":"nil-reference","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"}