t8y2/dbx · error

ETCD_V2_CAS_UNSUPPORTED

ETCD_V2_CAS_UNSUPPORTED

Error message

ETCD_V2_CAS_UNSUPPORTED: the etcd v2 API cannot compare createdIndex; use expectedModRevision

What it means

The v2 API's compare-and-swap can only compare modifiedIndex, not createdIndex. A put that supplies expectedCreateRevision with a non-zero value asks for a comparison v2 cannot perform, so it is rejected with this code; the message points to expectedModRevision as the supported alternative.

Source

Thrown at agents/drivers/etcd2-go/kv.go:209

	expectedModRevision := longOrNull(params, "expectedModRevision")
	expectedCreateRevision := longOrNull(params, "expectedCreateRevision")
	ttlValue := longOrNull(params, "ttl")
	preserveLease := boolOrDefault(params, "preserveLease", false)

	leaseValue := longOrNull(params, "lease")
	hasLease := leaseValue != nil
	hasTtl := ttlValue != nil
	if (hasLease && hasTtl) || (preserveLease && (hasLease || hasTtl)) {
		return nil, errors.New("lease, ttl, and preserveLease cannot be specified together")
	}
	if hasLease {
		return nil, errors.New("ETCD_V2_LEASE_UNSUPPORTED: the etcd v2 API has no lease objects; use ttl instead")
	}
	if preserveLease {
		return nil, errors.New("ETCD_V2_LEASE_UNSUPPORTED: the etcd v2 API has no lease objects; use ttl instead")
	}
	if expectedCreateRevision != nil && *expectedCreateRevision != 0 {
		return nil, errors.New("ETCD_V2_CAS_UNSUPPORTED: the etcd v2 API cannot compare createdIndex; use expectedModRevision")
	}
	if hasTtl && *ttlValue <= 0 {
		return nil, errors.New("ttl must be a positive integer")
	}

	form := url.Values{}
	form.Set("value", value)
	if hasTtl {
		form.Set("ttl", strconv.FormatInt(*ttlValue, 10))
	}
	if expectedModRevision != nil {
		form.Set("prevIndex", strconv.FormatInt(*expectedModRevision, 10))
	}
	if expectedCreateRevision != nil && *expectedCreateRevision == 0 {
		form.Set("prevExist", "false")
	}

	ctx, cancel := s.beginOperation()

View on GitHub (pinned to c0390bff16)

Solutions

  1. Replace expectedCreateRevision with expectedModRevision
  2. Drop the CAS field if create-revision semantics are not essential
  3. Use the v3 driver if compare-on-createRevision is required

Example fix

// before
{ "key":"k", "value":"v", "expectedCreateRevision": 42 }
// after
{ "key":"k", "value":"v", "expectedModRevision": 42 }
Defensive patterns

Strategy: validation

Validate before calling

if expectedCreateRevision != nil && *expectedCreateRevision != 0 {
	return errors.New("etcd v2 CAS supports only expectedModRevision")
}

Try / catch

_, err := session.handle(ctx, "put", params)
if err != nil && strings.Contains(err.Error(), "ETCD_V2_CAS_UNSUPPORTED") {
	return fmt.Errorf("rewrite CAS to expectedModRevision or use the v3 driver")
}

Prevention

When it happens

Trigger: Calling put with expectedCreateRevision set to a non-zero value against an etcd v2 session.

Common situations: Porting optimistic-concurrency code from v3 (where create_revision compare exists); generic clients that always send both expected fields; tests copied from v3 suites.

Related errors


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