{"record":{"id":"238f49592061eeb2","repo":"t8y2/dbx","slug":"etcd-unauthenticated","errorCode":"ETCD_UNAUTHENTICATED","errorMessage":"ETCD_UNAUTHENTICATED: authentication failed against %s","messagePattern":"ETCD_UNAUTHENTICATED: authentication failed against (.+?)","errorType":"error_code","errorClass":null,"httpStatus":401,"severity":"error","filePath":"agents/drivers/etcd2-go/client.go","lineNumber":269,"sourceCode":"\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\n\tcase http.StatusNotFound:\n\t\treturn nil, fmt.Errorf(\"ETCD_V2_API_DISABLED: %s does not expose the etcd v2 API (removed in etcd 3.6+)\", c.endpoint)\n\tcase http.StatusUnauthorized:\n\t\treturn nil, fmt.Errorf(\"ETCD_UNAUTHENTICATED: authentication failed against %s\", c.endpoint)\n\tdefault:\n\t\tbody, _ := io.ReadAll(io.LimitReader(response.Body, 4096))\n\t\treturn nil, fmt.Errorf(\"etcd v2 probe against %s failed: HTTP %d %s\", c.endpoint, response.StatusCode, strings.TrimSpace(string(body)))\n\t}\n}\n\n// do performs a v2 API request and returns the body. Non-2xx responses are\n// converted into etcdError values carrying the server's errorCode/message.\nfunc (c *authenticatedClient) do(ctx context.Context, method, path, body string, header map[string]string) ([]byte, *http.Response, error) {\n\tresponse, err := c.request(ctx, method, path, body, header)\n\tif err != nil {\n\t\treturn nil, response, err\n\t}\n\tpayload, readErr := io.ReadAll(response.Body)\n\t_ = response.Body.Close()\n\tif response.StatusCode < 200 || response.StatusCode >= 300 {\n\t\treturn nil, response, errorFromResponse(response.StatusCode, payload)\n\t}","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/etcd2-go/client.go#L251-L287","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Supply correct username/password in the etcd connection parameters used by the probe.","Verify credentials work directly: curl -u user:pass http://host:2379/version or etcdctl --user user:pass endpoint health.","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."],"exampleFix":"// before\nconnect({\"endpoint\": \"http://127.0.0.1:2379\"})\n// after\nconnect({\"endpoint\": \"http://127.0.0.1:2379\", \"username\": \"root\", \"password\": \"s3cret\"})","handlingStrategy":"validation","validationCode":"if !params.username || !params.password { return fmt.Errorf(\"probe skipped: etcd auth credentials not configured\") }","typeGuard":"func hasAuth(p map[string]any) bool { u, ok1 := p[\"username\"].(string); w, ok2 := p[\"password\"].(string); return ok1 && ok2 && u != \"\" && w != \"\" }","tryCatchPattern":"err := probeClient(ctx, cfg)\nif err != nil && strings.Contains(err.Error(), \"ETCD_UNAUTHENTICATED\") {\n    // refresh credentials from secret store and retry once\n}","preventionTips":["Store etcd credentials in a secret store and inject at connect time","Smoke-test credentials with curl -u before deploying","Alert on ETCD_UNAUTHENTICATED — it usually means rotated creds weren't propagated"],"tags":["etcd","authentication","http-401"],"backgroundTag":"http-401-unauthorized","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"}