{"record":{"id":"c49304b246716851","repo":"juicedata/juicefs","slug":"expect-1-keys-but-got-d","errorCode":null,"errorMessage":"expect 1 keys but got %d","messagePattern":"expect 1 keys but got (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/meta/tkv_etcd.go","lineNumber":58,"sourceCode":"\tobserved map[string]int64\n\tbuffer   map[string][]byte\n}\n\nfunc (tx *etcdTxn) get(key []byte) []byte {\n\tk := string(key)\n\tif v, ok := tx.buffer[k]; ok {\n\t\treturn v\n\t}\n\tresp, err := tx.kv.Get(tx.ctx, k, etcd.WithLimit(1))\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"get %v: %s\", k, err))\n\t}\n\tif resp.Count == 0 {\n\t\ttx.observed[k] = 0\n\t\treturn nil\n\t}\n\tif resp.Count > 1 {\n\t\tpanic(fmt.Errorf(\"expect 1 keys but got %d\", resp.Count))\n\t}\n\tfor _, pair := range resp.Kvs {\n\t\tif bytes.Equal(pair.Key, key) {\n\t\t\ttx.observed[k] = pair.ModRevision\n\t\t\treturn pair.Value\n\t\t} else {\n\t\t\tpanic(fmt.Errorf(\"expect key %v, but got %v\", k, string(pair.Key)))\n\t\t}\n\t}\n\tpanic(\"unreachable\")\n}\n\nfunc (tx *etcdTxn) gets(keys ...[]byte) [][]byte {\n\tif len(keys) > 128 {\n\t\tvar rs = make([][]byte, 0, len(keys))\n\t\tfor i := 0; i < len(keys); i += 128 {\n\t\t\trs = append(rs, tx.gets(keys[i:min(i+128, len(keys))]...)...)\n\t\t}","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/meta/tkv_etcd.go#L40-L76","documentation":"`etcdTxn.get` requests a key with `WithLimit(1)` and a range, then asserts it receives at most 1 KV. If etcd returns more than one pair for a point-style lookup (an internal invariant), the code panics: 'expect 1 keys but got N'. This guards against misuse of the Get options that would return multiple keys.","triggerScenarios":"An etcd Get issued for a single key unexpectedly returns multiple KVs — essentially only possible if the lookup is constructed as a range covering several keys, or etcd behaves unexpectedly.","commonSituations":"Corrupted or modified transaction code where the key argument is a prefix/range; a bug in etcd server or a proxy (e.g. gRPC gateway) mis-handling WithLimit; extremely rare, indicates an internal invariant violation rather than user error.","solutions":["Reproduce with logging on the exact key requested; confirm whether a range rather than exact key is being fetched","Upgrade JuiceFS to the latest release and etcd server to a supported version","File an issue with the JuiceFS maintainers including the etcd version and the key pattern if it reproduces"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// This panic indicates an internal invariant; capture stack and version info\nif strings.Contains(err.Error(), \"expect 1 keys but got\") {\n    log.Fatalf(\"etcd txn invariant violated, report issue with stack: %v\", err)\n}","preventionTips":["Run unmodified, released JuiceFS binaries","Keep etcd server on supported versions","If reproducing, collect key bytes and etcd version for a bug report"],"tags":["etcd","invariant","panic","transaction","juicefs"],"backgroundTag":"internal-invariant-violation","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}