t8y2/dbx · error

etcd v2 server did not report X-Etcd-Index

Error message

etcd v2 server did not report X-Etcd-Index

What it means

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.

Source

Thrown at agents/drivers/etcd2-go/watch.go:266

	return map[string]any{"watchId": watchID, "startedRevision": longString(waitIndex)}, nil
}

// currentEtcdIndex reads the X-Etcd-Index header from a lightweight GET.
func (c *authenticatedClient) currentEtcdIndex() (int64, error) {
	ctx, cancel := context.WithTimeout(context.Background(), operationTimeout)
	defer cancel()
	response, err := c.request(ctx, http.MethodGet, "/v2/keys/", "", nil)
	if err != nil {
		return 0, err
	}
	defer drainClose(response.Body)
	if response.StatusCode < 200 || response.StatusCode >= 300 {
		body, _ := io.ReadAll(io.LimitReader(response.Body, 4096))
		return 0, errorFromResponse(response.StatusCode, body)
	}
	index, err := strconv.ParseInt(response.Header.Get("X-Etcd-Index"), 10, 64)
	if err != nil {
		return 0, fmt.Errorf("etcd v2 server did not report X-Etcd-Index")
	}
	return index, nil
}

// pumpLongPollWatch drives the v2 wait loop: each response becomes one batch;
// timeouts and connection blips re-issue the same waitIndex.
func pumpLongPollWatch(ctx context.Context, client *authenticatedClient, key, scope string, waitIndex int64, includePrevKv bool, state *watchState) {
	for {
		if ctx.Err() != nil || state.hasTerminal() {
			return
		}
		query := url.Values{}
		query.Set("wait", "true")
		query.Set("waitIndex", strconv.FormatInt(waitIndex, 10))
		if scope == "prefix" {
			query.Set("recursive", "true")
		}
		pollCtx, pollCancel := context.WithTimeout(ctx, watchPollTimeout)

View on GitHub (pinned to c0390bff16)

Solutions

  1. Verify the endpoint is a genuine etcd v2 API server (curl -i and check X-Etcd-Index is present)
  2. Remove or fix proxies/load balancers that strip custom X-Etcd-* headers
  3. If using a test double, make the stub set X-Etcd-Index on responses
  4. Check etcd server version/upgrade notes for API changes

Example fix

// before (test stub)
w.Header().Set("Content-Type", "application/json")
// after
w.Header().Set("X-Etcd-Index", "42")
Defensive patterns

Strategy: validation

Validate before calling

resp, err := http.Get(etcdURL)
if err == nil && resp.Header.Get("X-Etcd-Index") == "" {
    // endpoint is not a compliant etcd v2 server; fix server/proxy first
}

Try / catch

index, err := currentEtcdIndex(resp)
if err != nil {
    return fmt.Errorf("endpoint does not look like etcd v2: %w", err)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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