t8y2/dbx · error

ETCD_UNAUTHENTICATED

ETCD_UNAUTHENTICATED

Error message

ETCD_UNAUTHENTICATED: authentication failed against %s

What it means

probeV2 returns this when the etcd v2 API endpoint answers HTTP 401 Unauthorized, meaning the credentials supplied for the probe were rejected. The library distinguishes authentication failure from other HTTP failures so the host can surface a clear, code-tagged (ETCD_UNAUTHENTICATED) error. It is thrown only from the v2 probe path in authenticatedClient's probe.

Source

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

// 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}, 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)
	}

View on GitHub (pinned to c0390bff16)

Solutions

  1. Supply correct username/password in the etcd connection parameters used by the probe.
  2. Verify credentials work directly: curl -u user:pass http://host:2379/version or etcdctl --user user:pass endpoint health.
  3. If the cluster is meant to be open, check whether auth was accidentally enabled (etcdctl auth status) and disable it or accept that credentials are required.

Example fix

// before
connect({"endpoint": "http://127.0.0.1:2379"})
// after
connect({"endpoint": "http://127.0.0.1:2379", "username": "root", "password": "s3cret"})
Defensive patterns

Strategy: validation

Validate before calling

if !params.username || !params.password { return fmt.Errorf("probe skipped: etcd auth credentials not configured") }

Type guard

func hasAuth(p map[string]any) bool { u, ok1 := p["username"].(string); w, ok2 := p["password"].(string); return ok1 && ok2 && u != "" && w != "" }

Try / catch

err := probeClient(ctx, cfg)
if err != nil && strings.Contains(err.Error(), "ETCD_UNAUTHENTICATED") {
    // refresh credentials from secret store and retry once
}

Prevention

When it happens

Trigger: Calling probeClient against an endpoint whose v2 API returns 401 — e.g. basic-auth credentials in the connection params are wrong, or the etcd instance requires auth (auth enabled) and none was provided.

Common situations: etcd cluster with auth enabled (etcdctl auth enable) probed with empty or stale username/password; rotated credentials not updated in agent config; connecting to a production cluster with auth using a dev config without credentials.

Understand the failure class

Related errors


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