{"record":{"id":"0942e938fe001d33","repo":"t8y2/dbx","slug":"unrecognized-etcd-version-response-from-s-w","errorCode":null,"errorMessage":"unrecognized etcd /version response from %s: %w","messagePattern":"unrecognized etcd /version response from (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/etcd2-go/client.go","lineNumber":247,"sourceCode":"\treturn client, probe, nil\n}\n\ntype etcdVersion struct {\n\tetcdserver  string\n\tetcdcluster string\n}\n\nfunc (c *authenticatedClient) fetchVersion(ctx context.Context) (etcdVersion, error) {\n\tbody, _, err := c.do(ctx, http.MethodGet, \"/version\", \"\", nil)\n\tif err != nil {\n\t\treturn etcdVersion{}, err\n\t}\n\tvar parsed struct {\n\t\tEtcdserver  string `json:\"etcdserver\"`\n\t\tEtcdcluster string `json:\"etcdcluster\"`\n\t}\n\tif err := json.Unmarshal(body, &parsed); err != nil {\n\t\treturn etcdVersion{}, fmt.Errorf(\"unrecognized etcd /version response from %s: %w\", c.endpoint, err)\n\t}\n\treturn etcdVersion{etcdserver: parsed.Etcdserver, etcdcluster: parsed.Etcdcluster}, nil\n}\n\n// probeV2 checks that the v2 keys API is actually served. A 403 proves the\n// channel and credentials reached etcd, mirroring the v3 agent's\n// PERMISSION_DENIED handling for restricted users.\nfunc (c *authenticatedClient) probeV2(ctx context.Context) (map[string]any, error) {\n\tresponse, err := c.request(ctx, http.MethodGet, \"/v2/members\", \"\", nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer drainClose(response.Body)\n\tswitch response.StatusCode {\n\tcase http.StatusOK:\n\t\treturn map[string]any{\"ok\": true, \"endpoint\": c.endpoint}, nil\n\tcase http.StatusForbidden:\n\t\treturn map[string]any{\"ok\": true, \"endpoint\": c.endpoint, \"limited\": true}, nil","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/etcd2-go/client.go#L229-L265","documentation":"fetchVersion GETs the /version endpoint and unmarshals the JSON body into fields etcdserver/etcdcluster. If the body is not JSON in etcd's expected shape (json.Unmarshal fails), the agent wraps the parse error into this message identifying the endpoint — meaning the server at that address is not speaking the etcd version API.","triggerScenarios":"probeClient/fetchVersion against an endpoint whose /version returns HTML (a proxy or web server error page), an empty body, a non-JSON error, or a JSON object without the expected keys.","commonSituations":"The endpoint URL actually points to a load balancer/ingress returning HTML 502 pages; a wrong port hitting a different service; a proxy intercepting requests and returning a captive/consent page; TLS terminating proxy serving its own error JSON schema.","solutions":["Verify the endpoint points at a real etcd server (curl the /version path and confirm JSON with etcdserver/etcdcluster fields)","Fix the endpoint/port in the connection config if it targets a proxy or the wrong service","Bypass or correctly configure intermediary proxies so /version reaches etcd unchanged"],"exampleFix":"// before\nclient := newClient(\"https://lb.example.com:443\") // LB returns HTML error pages\n\n// after\nclient := newClient(\"https://etcd-0.internal:2379\") // direct etcd endpoint\n// verify: curl https://etcd-0.internal:2379/version -> {\"etcdserver\":\"3.5.x\",...}","handlingStrategy":"try-catch","validationCode":"// preflight: confirm /version returns etcd-shaped JSON before using the endpoint\nresp, err := http.Get(endpoint + \"/version\")\nif err != nil { return err }\nvar v struct{ Etcdserver string `json:\"etcdserver\"` }\nbody, _ := io.ReadAll(resp.Body)\nif json.Unmarshal(body, &v) != nil || v.Etcdserver == \"\" {\n    return fmt.Errorf(\"%s is not an etcd endpoint\", endpoint)\n}","typeGuard":"func isBadVersionResponse(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"unrecognized etcd /version response\")\n}","tryCatchPattern":"v, err := client.fetchVersion(ctx)\nif isBadVersionResponse(err) {\n    return fmt.Errorf(\"endpoint %s did not return etcd /version JSON; check proxy/port config\", endpoint)\n}","preventionTips":["curl /version on new endpoints before adding them to config","Bypass LB/proxy error pages by targeting etcd members directly","Health-check endpoints out of rotation when they return non-etcd responses"],"tags":["go","etcd","http","protocol","version-negotiation"],"backgroundTag":"unexpected-server-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"}