t8y2/dbx · error
lease, ttl, and preserveLease cannot be specified together
Error message
lease, ttl, and preserveLease cannot be specified together
What it means
Input-validation error thrown by the put operation at kv.go:250. The etcd lease model for a put accepts exactly one lease source: an existing lease ID (lease), a fresh lease granted from a TTL (ttl), or preserving the key's current lease (preserveLease). Supplying more than one of these together is ambiguous, so the driver rejects the call before touching etcd.
Source
Thrown at agents/drivers/etcd-go/kv.go:250
return nil, err
}
key, err := keyBytesParam(params)
if err != nil {
return nil, err
}
value, err := parseValueObject(rawObject(params, "value"))
if err != nil {
return nil, err
}
expectedModRevision := longOrNull(params, "expectedModRevision")
expectedCreateRevision := longOrNull(params, "expectedCreateRevision")
leaseValue := longOrNull(params, "lease")
ttlValue := longOrNull(params, "ttl")
preserveLease := boolOrDefault(params, "preserveLease", false)
hasLease := leaseValue != nil
hasTtl := ttlValue != nil
if (hasLease && hasTtl) || (preserveLease && (hasLease || hasTtl)) {
return nil, errors.New("lease, ttl, and preserveLease cannot be specified together")
}
ctx, cancel := s.beginOperation()
defer s.endOperation(cancel)
if preserveLease {
revision, err := putPreservingLease(client, ctx, key, value)
if err != nil {
return nil, err
}
return map[string]any{"revision": longString(revision)}, nil
}
var leaseID clientv3.LeaseID
var grantedLeaseID clientv3.LeaseID
if hasTtl {
if *ttlValue <= 0 {
return nil, errors.New("ttl must be a positive integer")
}View on GitHub (pinned to c0390bff16)
Solutions
- Remove all but one of lease, ttl, preserveLease from the put params — keep exactly one lease strategy (or none for a permanent key).
- If you want the key to keep its existing lease, pass preserveLease:true and drop lease/ttl.
- If you need a fresh expiring key, pass ttl only and omit lease/preserveLease.
- If attaching to a pre-granted lease, pass lease (the lease ID number) only.
Example fix
// before
put(session, key, value, { ttl: 60, preserveLease: true })
// after
put(session, key, value, { preserveLease: true }) Defensive patterns
Strategy: validation
Validate before calling
function validatePutLeaseOptions(opts) {
const n = [opts.lease != null, opts.ttl != null, opts.preserveLease === true].filter(Boolean).length;
if (n > 1) throw new Error('specify at most one of lease, ttl, preserveLease');
} Type guard
function hasConflictingLeaseOptions(p) {
return (p.lease != null && p.ttl != null) || (p.preserveLease === true && (p.lease != null || p.ttl != null));
} Try / catch
try { return put(session, key, value, opts); }
catch (e) {
if (String(e).includes('cannot be specified together')) {
const { lease, ttl, ...rest } = opts;
return put(session, key, value, rest); // drop conflicting lease opts
}
throw e;
} Prevention
- Centralize put option building so lease strategy is chosen in one place.
- Never merge default lease options with user-supplied ones blindly.
- Pick one idiom per call site: ttl for expiring keys, preserveLease for updates, lease for pre-granted IDs.
- Add a unit test asserting conflicting combos are rejected.
When it happens
Trigger: Any put call whose params include both lease and ttl; or preserveLease=true together with either lease or ttl.
Common situations: Template/config merging that accumulates lease options from multiple layers (default ttl plus explicit lease); migrating code from ttl-based puts to preserveLease and leaving the old parameter in place; copy-pasted option objects.
Related errors
- ttl must be a positive integer
- ETCD_NEWKEY_REQUIRED
- id must be a positive integer or 0
- ETCD_INVALID_%s
- user is required
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/55e3ac1b8c4c060b.
Report an issue: GitHub.