t8y2/dbx · error
unrecognized etcd /version response from %s: %w
Error message
unrecognized etcd /version response from %s: %w
What it means
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.
Source
Thrown at agents/drivers/etcd2-go/client.go:247
return client, probe, nil
}
type etcdVersion struct {
etcdserver string
etcdcluster string
}
func (c *authenticatedClient) fetchVersion(ctx context.Context) (etcdVersion, error) {
body, _, err := c.do(ctx, http.MethodGet, "/version", "", nil)
if err != nil {
return etcdVersion{}, err
}
var parsed struct {
Etcdserver string `json:"etcdserver"`
Etcdcluster string `json:"etcdcluster"`
}
if err := json.Unmarshal(body, &parsed); err != nil {
return etcdVersion{}, fmt.Errorf("unrecognized etcd /version response from %s: %w", c.endpoint, err)
}
return etcdVersion{etcdserver: parsed.Etcdserver, etcdcluster: parsed.Etcdcluster}, nil
}
// probeV2 checks that the v2 keys API is actually served. A 403 proves the
// channel and credentials reached etcd, mirroring the v3 agent's
// PERMISSION_DENIED handling for restricted users.
func (c *authenticatedClient) probeV2(ctx context.Context) (map[string]any, error) {
response, err := c.request(ctx, http.MethodGet, "/v2/members", "", nil)
if err != nil {
return nil, err
}
defer drainClose(response.Body)
switch response.StatusCode {
case http.StatusOK:
return map[string]any{"ok": true, "endpoint": c.endpoint}, nil
case http.StatusForbidden:
return map[string]any{"ok": true, "endpoint": c.endpoint, "limited": true}, nilView on GitHub (pinned to c0390bff16)
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
Example fix
// before
client := newClient("https://lb.example.com:443") // LB returns HTML error pages
// after
client := newClient("https://etcd-0.internal:2379") // direct etcd endpoint
// verify: curl https://etcd-0.internal:2379/version -> {"etcdserver":"3.5.x",...} Defensive patterns
Strategy: try-catch
Validate before calling
// preflight: confirm /version returns etcd-shaped JSON before using the endpoint
resp, err := http.Get(endpoint + "/version")
if err != nil { return err }
var v struct{ Etcdserver string `json:"etcdserver"` }
body, _ := io.ReadAll(resp.Body)
if json.Unmarshal(body, &v) != nil || v.Etcdserver == "" {
return fmt.Errorf("%s is not an etcd endpoint", endpoint)
} Type guard
func isBadVersionResponse(err error) bool {
return err != nil && strings.Contains(err.Error(), "unrecognized etcd /version response")
} Try / catch
v, err := client.fetchVersion(ctx)
if isBadVersionResponse(err) {
return fmt.Errorf("endpoint %s did not return etcd /version JSON; check proxy/port config", endpoint)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ZooKeeper sent an unexpected token after GSSAPI completion
- ETCD_WATCH_SCOPE_INVALID
- ETCD_WATCH_NOT_FOUND
- lease, ttl, and preserveLease cannot be specified together
- ZooKeeper connection is nil
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/0942e938fe001d33.
Report an issue: GitHub.