{"record":{"id":"c6a2197487f7d747","repo":"etcd-io/etcd","slug":"unexpected-mod-revision-filter-in-put","errorCode":null,"errorMessage":"unexpected mod revision filter in put","messagePattern":"unexpected mod revision filter in put","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/v3/op.go","lineNumber":318,"sourceCode":"// OpPut returns \"put\" operation based on given key-value and operation options.\nfunc OpPut(key, val string, opts ...OpOption) Op {\n\tret := Op{t: tPut, key: []byte(key), val: []byte(val)}\n\tret.applyOpts(opts)\n\tswitch {\n\tcase ret.end != nil:\n\t\tpanic(\"unexpected range in put\")\n\tcase ret.limit != 0:\n\t\tpanic(\"unexpected limit in put\")\n\tcase ret.rev != 0:\n\t\tpanic(\"unexpected revision in put\")\n\tcase ret.sort != nil:\n\t\tpanic(\"unexpected sort in put\")\n\tcase ret.serializable:\n\t\tpanic(\"unexpected serializable in put\")\n\tcase ret.countOnly:\n\t\tpanic(\"unexpected countOnly in put\")\n\tcase ret.minModRev != 0, ret.maxModRev != 0:\n\t\tpanic(\"unexpected mod revision filter in put\")\n\tcase ret.minCreateRev != 0, ret.maxCreateRev != 0:\n\t\tpanic(\"unexpected create revision filter in put\")\n\tcase ret.filterDelete, ret.filterPut:\n\t\tpanic(\"unexpected filter in put\")\n\tcase ret.createdNotify:\n\t\tpanic(\"unexpected createdNotify in put\")\n\t}\n\treturn ret\n}\n\n// OpTxn returns \"txn\" operation based on given transaction conditions.\nfunc OpTxn(cmps []Cmp, thenOps []Op, elseOps []Op) Op {\n\tclonedCmps := make([]Cmp, len(cmps))\n\tfor i := range cmps {\n\t\tclonedCmps[i] = cmps[i].Clone()\n\t}\n\treturn Op{t: tTxn, cmps: clonedCmps, thenOps: thenOps, elseOps: elseOps}\n}","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/etcd-io/etcd/blob/f744d457f484e9f748a0700b48ef96dcf792df33/client/v3/op.go#L300-L336","documentation":"OpPut panics with 'unexpected mod revision filter in put' when a modification-revision filter (WithMinModRev or WithMaxModRev) is applied to a put. These filters restrict range results to keys modified within a revision window and are only valid on queries. OpPut validates its option set up front and panics on any query-only option.","triggerScenarios":"Calling clientv3.OpPut(key, val, clientv3.WithMinModRev(n)) or WithMaxModRev(n). Commonly happens when options prepared for a filtered Get (e.g. watching history or incremental sync) are forwarded verbatim to a put in the same helper or code path.","commonSituations":"Historical/incremental sync utilities that read with revision windows and write back cached entries; helper functions taking a single variadic OpOption list used for both reads and writes; copy-paste between adjacent Get and Put calls.","solutions":["Remove WithMinModRev/WithMaxModRev from the put call","If you intended optimistic concurrency (write only if the key is at revision N), use a Txn with If(ModRevision(key) = N) and the put in Then instead","Split read vs write option slices in shared helpers","Verify no other filter options (WithMinCreateRev, WithMaxCreateRev, WithFilter*) ride along in the same slice"],"exampleFix":"// before\nop := clientv3.OpPut(\"k\", \"v\", clientv3.WithMinModRev(42))\n\n// after (optimistic write gated on revision)\nresp, err := cli.Txn(ctx).\n\tIf(clientv3.Compare(clientv3.ModRevision(\"k\"), \"=\", 42)).\n\tThen(clientv3.OpPut(\"k\", \"v\")).\n\tCommit()","handlingStrategy":"validation","validationCode":"// If the intent is revision-gated write, use Compare instead of filters:\nfunc putIfModRev(ctx context.Context, cli *clientv3.Client, k, v string, rev int64) (*clientv3.TxnResponse, error) {\n\treturn cli.Txn(ctx).\n\t\tIf(clientv3.Compare(clientv3.ModRevision(k), \"=\", rev)).\n\t\tThen(clientv3.OpPut(k, v)).\n\t\tCommit()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Map 'write only if at revision N' to Compare(ModRevision), not WithMinModRev","Keep revision-window filters on reads","Split read/write option assembly"],"tags":["etcd","clientv3","panic","api-misuse","put","revision-filter"],"backgroundTag":null,"analyzedSha":"f744d457f484e9f748a0700b48ef96dcf792df33","analyzedAt":"2026-08-15T09:39:50.079Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}