{"record":{"id":"b02ebbe897b043ae","repo":"t8y2/dbx","slug":"cannot-preserve-lease-key-does-not-exist-or-has-n","errorCode":null,"errorMessage":"Cannot preserve lease: key does not exist or has no lease","messagePattern":"Cannot preserve lease: key does not exist or has no lease","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/etcd-go/kv.go","lineNumber":320,"sourceCode":"\t\treturn response.Header.Revision, nil\n\t}()\n\tif err != nil && grantedLeaseID != 0 {\n\t\t_, _ = client.Lease.Revoke(context.Background(), grantedLeaseID)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn map[string]any{\"revision\": longString(revision)}, nil\n}\n\nfunc putPreservingLease(client *clientv3.Client, ctx context.Context, key, value string) (int64, error) {\n\tfor attempt := 0; attempt < preserveLeaseMaxAttempts; attempt++ {\n\t\texisting, err := client.Get(ctx, key)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif len(existing.Kvs) == 0 || existing.Kvs[0].Lease <= 0 {\n\t\t\treturn 0, errors.New(\"Cannot preserve lease: key does not exist or has no lease\")\n\t\t}\n\t\tcurrent := existing.Kvs[0]\n\t\ttxn := client.Txn(ctx).\n\t\t\tIf(clientv3.Compare(clientv3.ModRevision(key), \"=\", current.ModRevision)).\n\t\t\tThen(clientv3.OpPut(key, value, clientv3.WithLease(clientv3.LeaseID(current.Lease))))\n\t\tresponse, err := txn.Commit()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif response.Succeeded {\n\t\t\treturn response.Header.Revision, nil\n\t\t}\n\t}\n\treturn 0, errors.New(\"Cannot preserve lease: key changed concurrently; retry the save\")\n}\n\nfunc (s *etcdSession) delete(params map[string]json.RawMessage) (any, error) {\n\tclient, err := s.activeClient()","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/etcd-go/kv.go#L302-L338","documentation":"Thrown by putPreservingLease (kv.go:320), reached when put is called with preserveLease:true. Before writing, the function GETs the key to learn its current lease; if the key does not exist or exists with no attached lease (Lease <= 0), there is nothing to preserve and this error is returned. Lease preservation only makes sense for keys already owned by an active lease.","triggerScenarios":"put(..., { preserveLease: true }) on a key that was never created with a lease/ttl, or on a key whose lease already expired (etcd auto-deleted the key), or on a not-yet-existing key.","commonSituations":"Using preserveLease as a blanket default for all writes including initial creation; lease TTL expiring between writes so the key vanishes; key previously written without ttl and code assumes it has one; key created by another tool without a lease.","solutions":["Ensure the key exists with an active lease before using preserveLease — create it once with { ttl: N } first.","Fall back to a plain put (no preserveLease) when the key is absent, e.g. get first and branch.","Check the key's metadata lease via get before every preserveLease put.","If the lease expired, re-grant it with ttl instead of trying to preserve it.","Add a refresh loop (keepalive) for the lease if keys must outlive the TTL."],"exampleFix":"// before\nput(session, key, value, { preserveLease: true }) // key missing -> error\n// after\nconst cur = get(session, key);\nif (cur.found && cur.metadata.lease) put(session, key, value, { preserveLease: true });\nelse put(session, key, value, { ttl: 60 });","handlingStrategy":"validation","validationCode":"const cur = get(session, key);\nif (!cur.found || !cur.metadata.lease) {\n  throw new Error('preserveLease requires an existing key with an active lease');\n}","typeGuard":"function canPreserveLease(keyState) { return keyState.found === true && keyState.metadata != null && keyState.metadata.lease > 0; }","tryCatchPattern":"try { return put(session, key, value, { preserveLease: true }); }\ncatch (e) {\n  if (String(e).includes('key does not exist or has no lease'))\n    return put(session, key, value, { ttl: 60 }); // bootstrap the lease\n  throw e;\n}","preventionTips":["Create keys with { ttl: N } at least once before any preserveLease writes.","Refresh/renew leases (keepalive) so keys don't silently expire.","get() with metadata and check lease > 0 before preserveLease in shared helpers.","Don't enable preserveLease as a blanket default for all writes."],"tags":["etcd","lease","preserve-lease","precondition"],"backgroundTag":"lease-not-found","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}