{"record":{"id":"6bd5d97f100e1ae3","repo":"t8y2/dbx","slug":"parse-zookeeper-tls-address-q-w","errorCode":null,"errorMessage":"parse ZooKeeper TLS address %q: %w","messagePattern":"parse ZooKeeper TLS address %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/argo-go/zookeeper_protocol.go","lineNumber":59,"sourceCode":"var newZooKeeperSASLClient = func(host string, config connectionConfig) (zooKeeperSASLClient, error) {\n\tservice, options := zooKeeperGSSAPIOptions(config)\n\tmechanism, err := gosasl.NewGSSAPIMechanismWithOptions(service, options)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn gosasl.NewSaslClient(host, mechanism), nil\n}\n\nvar dialZooKeeperConnection = func(address string, timeout time.Duration, tlsConfig *tls.Config) (net.Conn, error) {\n\tdialer := &net.Dialer{Timeout: timeout}\n\tif tlsConfig == nil {\n\t\treturn dialer.Dial(\"tcp\", address)\n\t}\n\tconfig := tlsConfig.Clone()\n\tif config.ServerName == \"\" {\n\t\thost, _, err := net.SplitHostPort(address)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"parse ZooKeeper TLS address %q: %w\", address, err)\n\t\t}\n\t\tconfig.ServerName = host\n\t}\n\treturn tls.DialWithDialer(dialer, \"tcp\", address, config)\n}\n\nvar shuffleZooKeeperServers = func(servers []string) {\n\trand.Shuffle(len(servers), func(first, second int) {\n\t\tservers[first], servers[second] = servers[second], servers[first]\n\t})\n}\n\nfunc zooKeeperGSSAPIOptions(config connectionConfig) (string, gosasl.GSSAPIOptions) {\n\tservice := firstNonEmpty(config.ZooKeeperKerberos.Service, \"zookeeper\")\n\toptions := gssapiOptionsFromKerberos(config.Kerberos)\n\toptions.QOP = \"auth\"\n\toptions.AuthorizationID = \"\"\n\toptions.ServiceHost = \"\"","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/argo-go/zookeeper_protocol.go#L41-L77","documentation":"Wrapped during a TLS connection to a ZooKeeper ensemble: when the tls.Config has no explicit ServerName, the client derives it from the address via net.SplitHostPort. If the address is malformed (no host:port, brackets wrong, extra characters), SplitHostPort fails and the dial aborts with this error. It surfaces the underlying parse error plus the offending address string.","triggerScenarios":"Calling the TLS dial path in zookeeper_protocol.go with an address string that net.SplitHostPort cannot parse — e.g. missing port ('zk1:'), an empty string, an unbalanced IPv6 literal ('[::1'), or a host containing stray characters before TLS dial when config.ServerName is empty.","commonSituations":"ZooKeeper connect string assembled by hand or from a misparsed config value; trimming the port accidentally when substituting hosts; IPv6 ensemble addresses not bracketed; environment variable containing a bare hostname without ':2181'.","solutions":["Correct the address to valid 'host:port' form (e.g. 'zk1.example.com:2181', '[2001:db8::1]:2181')","If the config intentionally omits the port, append the default ZooKeeper port 2181 before dialing","Alternatively set tlsConfig.ServerName explicitly in code so the SplitHostPort fallback is skipped","Validate addresses with net.SplitHostPort at config load time and fail early with a clear message"],"exampleFix":"// before\naddress := \"zk1.example.com\"\nconn, err := tlsConnect(address)\n// after\naddress := \"zk1.example.com:2181\"\nif _, _, err := net.SplitHostPort(address); err != nil {\n    return fmt.Errorf(\"invalid ZooKeeper address %q: %w\", address, err)\n}\nconn, err := tlsConnect(address)","handlingStrategy":"validation","validationCode":"func validZKAddress(addr string) error {\n    _, _, err := net.SplitHostPort(addr)\n    return err\n}","typeGuard":"func isHostPort(s string) bool {\n    host, port, err := net.SplitHostPort(s)\n    return err == nil && host != \"\" && port != \"\"\n}","tryCatchPattern":"conn, err := tlsConnect(address)\nif err != nil {\n    var parseErr *net.AddrError\n    if errors.As(err, &parseErr) && parseErr.Err == \"missing port in address\" {\n        address = net.JoinHostPort(strings.TrimSuffix(address, \":\"), \"2181\")\n        conn, err = tlsConnect(address)\n    }\n    if err != nil { return err }\n}","preventionTips":["Always store ZooKeeper endpoints as host:port, validated at config load with net.SplitHostPort","Use net.JoinHostPort to build addresses (handles IPv6 brackets)","Set tlsConfig.ServerName explicitly to skip the address parsing fallback","Test TLS connectivity to the ensemble during deployment smoke checks"],"tags":["tls","zookeeper","address-parsing","network"],"backgroundTag":"invalid-address-format","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"}