t8y2/dbx · error
etcd v2 probe against %s failed: HTTP %d %s
Error message
etcd v2 probe against %s failed: HTTP %d %s
What it means
This is the fallback error from probeV2: the v2 API endpoint returned an HTTP status other than 200, 403, 404, or 401. The message includes the status code and up to 4KB of the response body so the underlying server-side failure is visible. It wraps whatever non-happy-path HTTP response the probe got.
Source
Thrown at agents/drivers/etcd2-go/client.go:272
// 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}, nil
case http.StatusNotFound:
return nil, fmt.Errorf("ETCD_V2_API_DISABLED: %s does not expose the etcd v2 API (removed in etcd 3.6+)", c.endpoint)
case http.StatusUnauthorized:
return nil, fmt.Errorf("ETCD_UNAUTHENTICATED: authentication failed against %s", c.endpoint)
default:
body, _ := io.ReadAll(io.LimitReader(response.Body, 4096))
return nil, fmt.Errorf("etcd v2 probe against %s failed: HTTP %d %s", c.endpoint, response.StatusCode, strings.TrimSpace(string(body)))
}
}
// do performs a v2 API request and returns the body. Non-2xx responses are
// converted into etcdError values carrying the server's errorCode/message.
func (c *authenticatedClient) do(ctx context.Context, method, path, body string, header map[string]string) ([]byte, *http.Response, error) {
response, err := c.request(ctx, method, path, body, header)
if err != nil {
return nil, response, err
}
payload, readErr := io.ReadAll(response.Body)
_ = response.Body.Close()
if response.StatusCode < 200 || response.StatusCode >= 300 {
return nil, response, errorFromResponse(response.StatusCode, payload)
}
if readErr != nil {
return nil, response, readErr
}View on GitHub (pinned to c0390bff16)
Solutions
- Read the HTTP status and body in the message to identify the server-side cause.
- Check cluster health: etcdctl endpoint health / endpoint status; look for 'no leader' or low quorum.
- If a proxy sits in front of etcd, bypass it or fix its routing/health checks for port 2379.
- Retry after the cluster recovers; this condition is often transient.
Defensive patterns
Strategy: retry
Validate before calling
resp, err := http.Get(endpoint + "/health")
if err != nil || resp.StatusCode != http.StatusOK { return fmt.Errorf("endpoint not healthy, aborting probe") } Try / catch
err := probeClient(ctx, cfg)
if err != nil && strings.Contains(err.Error(), "failed: HTTP 5") {
return retryWithBackoff(ctx, 3, probeClient, cfg) // transient 5xx
} Prevention
- Check etcdctl endpoint health before probing
- Bypass or correctly configure proxies in front of etcd
- Use exponential backoff — 503 during leader election is common
When it happens
Trigger: probeClient receives statuses like 500 (etcd panics/overloaded), 503 (no leader / cluster unavailable), 400, or a proxy's HTML error page on the v2 probe request.
Common situations: etcd cluster without a leader (post-failure election), reverse proxy or load balancer returning 502/503, etcd still starting up, TLS-terminating proxy misrouting the request.
Related errors
- unrecognized etcd /version response from %s: %w
- user is required
- Not connected
- Client certificate and key must be provided together
- No etcd endpoint configured
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/59907bc837f22d8d.
Report an issue: GitHub.