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
- Replace expectedCreateRevision with expectedModRevision
- Drop the CAS field if create-revision semantics are not essential
- 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
- Use expectedModRevision for all v2 optimistic concurrency
- Do not blindly forward v3-style CAS fields to v2 sessions
- Read the mod_revision from get results and use it for compares
- Pin v3-dependent workflows to the v3 driver via a capability flag
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.