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

  1. Remove all but one of lease, ttl, preserveLease from the put params — keep exactly one lease strategy (or none for a permanent key).
  2. If you want the key to keep its existing lease, pass preserveLease:true and drop lease/ttl.
  3. If you need a fresh expiring key, pass ttl only and omit lease/preserveLease.
  4. 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

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


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