t8y2/dbx · error

ETCD_HISTORY_FAILED

ETCD_HISTORY_FAILED

Error message

ETCD_HISTORY_FAILED: 

What it means

Generic wrapper for any history-walk failure that is not ErrCompacted. In the history operation, if the retry loop records a historyFailure, the driver returns 'ETCD_HISTORY_FAILED: <underlying error>' at history.go:201, propagating the raw etcd clientv3 error text after the prefix. It means the incremental revision walk over the key's revisions failed server-side or on the wire.

Source

Thrown at agents/drivers/etcd-go/history.go:201

	_ = client.RequestProgress(progressCtx)
	progressCancel()
	select {
	case <-completed:
	case <-time.After(15 * time.Second):
		return nil, errors.New("ETCD_HISTORY_TIMEOUT: history replay did not reach the requested revision")
	}

	failureMu.Lock()
	historyFailure := failure
	failureMu.Unlock()
	if historyFailure != nil {
		if errors.Is(historyFailure, rpctypes.ErrCompacted) {
			if revision, ok := compactedRevisionOf(client, key); ok {
				return nil, errors.New("ETCD_COMPACTED: requested history was compacted at revision " + longString(revision))
			}
			return nil, errors.New("ETCD_COMPACTED: requested history was compacted")
		}
		return nil, errors.New("ETCD_HISTORY_FAILED: " + historyFailure.Error())
	}

	collector.mu.Lock()
	rows := make([]map[string]any, len(collector.rows))
	copy(rows, collector.rows)
	truncated := collector.truncated
	collector.mu.Unlock()
	sort.SliceStable(rows, func(i, j int) bool {
		a, _ := strconv.ParseInt(rows[i]["revision"].(string), 10, 64)
		b, _ := strconv.ParseInt(rows[j]["revision"].(string), 10, 64)
		return a > b
	})
	serialized := make([]any, len(rows))
	for i, row := range rows {
		serialized[i] = row
	}
	return map[string]any{
		"events":           serialized,

View on GitHub (pinned to c0390bff16)

Solutions

  1. Read the text after the 'ETCD_HISTORY_FAILED: ' prefix to identify the underlying etcd error and apply that error's own remedy.
  2. Retry the history call — transient Unavailable/deadline failures often clear once the etcd member recovers.
  3. Check etcd cluster health (etcdctl endpoint health) and network connectivity to the member.
  4. Verify the client's RBAC role permits reading the key range.
  5. Increase the driver's rpc timeout or lower the history limit to shrink the walk.

Example fix

// before
const rows = history(session, key); // fails opaque 'ETCD_HISTORY_FAILED: ...'
// after
try { const rows = history(session, key, { limit: 50 }); }
catch (e) {
  if (String(e).startsWith('ETCD_HISTORY_FAILED:')) retryWithBackoff();
  else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

const healthy = await checkClusterHealth(); // etcdctl endpoint health equivalent
if (!healthy) throw new Error('etcd cluster unhealthy; aborting history query');

Type guard

function isHistoryFailedError(e) { return String(e && e.message || e).startsWith('ETCD_HISTORY_FAILED:'); }

Try / catch

try { return history(session, key, { limit: 100 }); }
catch (e) {
  if (isHistoryFailedError(e) && isTransient(String(e).split('ETCD_HISTORY_FAILED: ')[1]))
    return retryWithBackoff(() => history(session, key));
  throw e;
}

Prevention

When it happens

Trigger: Calling the history operation when an underlying Get/revision-walk fails with a non-compaction etcd error: e.g. ErrGRPCUnavailable (server down), context deadline exceeded (rpcTimeoutSeconds), ErrGRPCPermissionDenied, or a transient gRPC stream failure mid-walk.

Common situations: etcd node being restarted or unreachable under load; network partition between driver and cluster; RBAC user lacking read permission on the key; request exceeding the driver's per-RPC timeout on very long history walks.

Related errors


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