{"record":{"id":"5f0a06e4e86af914","repo":"t8y2/dbx","slug":"etcd-v2-server-did-not-report-x-etcd-index","errorCode":null,"errorMessage":"etcd v2 server did not report X-Etcd-Index","messagePattern":"etcd v2 server did not report X-Etcd-Index","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/etcd2-go/watch.go","lineNumber":266,"sourceCode":"\treturn map[string]any{\"watchId\": watchID, \"startedRevision\": longString(waitIndex)}, nil\n}\n\n// currentEtcdIndex reads the X-Etcd-Index header from a lightweight GET.\nfunc (c *authenticatedClient) currentEtcdIndex() (int64, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), operationTimeout)\n\tdefer cancel()\n\tresponse, err := c.request(ctx, http.MethodGet, \"/v2/keys/\", \"\", nil)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer drainClose(response.Body)\n\tif response.StatusCode < 200 || response.StatusCode >= 300 {\n\t\tbody, _ := io.ReadAll(io.LimitReader(response.Body, 4096))\n\t\treturn 0, errorFromResponse(response.StatusCode, body)\n\t}\n\tindex, err := strconv.ParseInt(response.Header.Get(\"X-Etcd-Index\"), 10, 64)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"etcd v2 server did not report X-Etcd-Index\")\n\t}\n\treturn index, nil\n}\n\n// pumpLongPollWatch drives the v2 wait loop: each response becomes one batch;\n// timeouts and connection blips re-issue the same waitIndex.\nfunc pumpLongPollWatch(ctx context.Context, client *authenticatedClient, key, scope string, waitIndex int64, includePrevKv bool, state *watchState) {\n\tfor {\n\t\tif ctx.Err() != nil || state.hasTerminal() {\n\t\t\treturn\n\t\t}\n\t\tquery := url.Values{}\n\t\tquery.Set(\"wait\", \"true\")\n\t\tquery.Set(\"waitIndex\", strconv.FormatInt(waitIndex, 10))\n\t\tif scope == \"prefix\" {\n\t\t\tquery.Set(\"recursive\", \"true\")\n\t\t}\n\t\tpollCtx, pollCancel := context.WithTimeout(ctx, watchPollTimeout)","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/etcd2-go/watch.go#L248-L284","documentation":"currentEtcdIndex reads the X-Etcd-Index response header to learn the store's current revision. If the header is missing or not a valid integer, the function cannot determine the index and returns this error. The library requires a spec-compliant etcd v2 server that always sets this header on non-wait responses.","triggerScenarios":"Issuing an HTTP GET to an etcd v2 endpoint whose response lacks a parseable X-Etcd-Index header — the server is not really etcd v2, a proxy strips the header, or an empty/erroneous cluster response still returned a 2xx status.","commonSituations":"Pointing the driver at a mock/compat layer (e.g. an API gateway or v3-only server) that does not emit X-Etcd-Index; misconfigured load balancer dropping custom headers; testing against a stub HTTP server that forgot the header.","solutions":["Verify the endpoint is a genuine etcd v2 API server (curl -i and check X-Etcd-Index is present)","Remove or fix proxies/load balancers that strip custom X-Etcd-* headers","If using a test double, make the stub set X-Etcd-Index on responses","Check etcd server version/upgrade notes for API changes"],"exampleFix":"// before (test stub)\nw.Header().Set(\"Content-Type\", \"application/json\")\n// after\nw.Header().Set(\"X-Etcd-Index\", \"42\")","handlingStrategy":"validation","validationCode":"resp, err := http.Get(etcdURL)\nif err == nil && resp.Header.Get(\"X-Etcd-Index\") == \"\" {\n    // endpoint is not a compliant etcd v2 server; fix server/proxy first\n}","typeGuard":null,"tryCatchPattern":"index, err := currentEtcdIndex(resp)\nif err != nil {\n    return fmt.Errorf(\"endpoint does not look like etcd v2: %w\", err)\n}","preventionTips":["curl -i the endpoint and confirm X-Etcd-Index on responses","Avoid proxies that strip X-Etcd-* headers","Point at etcd 2.x API paths (/v2/keys), not v3 gateways","Ensure test stubs set the header"],"tags":["etcd","http-header","compatibility"],"backgroundTag":"missing-response-header","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}