t8y2/dbx · error

etcd connection error: %w

Error message

etcd connection error: %w

What it means

requestAt wraps any transport-level failure of the HTTP request (DNS failure, connection refused/reset, TLS handshake error, timeout) into the sentinel-prefixed error 'etcd connection error: %w'. It is deliberately gRPC-style so host-side transient-error handling can keep classifying it as a connection problem. No HTTP response was received at all.

Source

Thrown at agents/drivers/etcd2-go/client.go:320

		reader = strings.NewReader(body)
	}
	req, err := http.NewRequestWithContext(ctx, method, strings.TrimSuffix(endpoint, "/")+path, reader)
	if err != nil {
		return nil, err
	}
	if c.username != "" {
		req.SetBasicAuth(c.username, c.password)
	}
	for key, value := range header {
		req.Header.Set(key, value)
	}
	if body != "" && req.Header.Get("Content-Type") == "" {
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	}
	response, err := c.http.Do(req)
	if err != nil {
		// gRPC-style sentinel so the host-side transient-error handling keeps working.
		return nil, fmt.Errorf("etcd connection error: %w", err)
	}
	return response, nil
}

// doAt performs a v2 API request against a specific endpoint.
func (c *authenticatedClient) doAt(ctx context.Context, method, endpoint, path, body string) ([]byte, *http.Response, error) {
	response, err := c.requestAt(ctx, method, endpoint, path, body, nil)
	if err != nil {
		return nil, nil, 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

  1. Inspect the wrapped %w cause to distinguish refused vs timeout vs TLS failure.
  2. Verify the endpoint is reachable: curl http://host:2379/version (or https as configured).
  3. Confirm the client port 2379 (not the peer port 2380) and correct scheme http vs https.
  4. Retry with backoff for transient network conditions; the prefix supports the library's transient-error classification.

Example fix

// before
endpoint = "https://127.0.0.1:2379" // server has no TLS
// after
endpoint = "http://127.0.0.1:2379"
Defensive patterns

Strategy: retry

Validate before calling

conn, err := net.DialTimeout("tcp", host, 2*time.Second)
if err != nil { return fmt.Errorf("etcd endpoint %s unreachable: %v", host, err) }
conn.Close()

Try / catch

_, err := client.request(ctx, "GET", "/keys/foo")
if err != nil && strings.HasPrefix(err.Error(), "etcd connection error:") {
    return retryWithBackoff(ctx, 5, func() error { _, err := client.request(ctx, "GET", "/keys/foo"); return err })
}

Prevention

When it happens

Trigger: Any v2 API call (via request or doAt) where c.http.Do fails: endpoint unreachable, port closed, TLS mismatch, network partition, or context deadline exceeded.

Common situations: etcd not running on the configured host:port, wrong port (2379 client vs 2380 peer), firewall blocking, TLS config pointing at a plaintext endpoint or vice versa, container networking issues.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/a34291d10c8a8aa5. Report an issue: GitHub.