{"id":"20b98b2e57269048","repo":"go-redis/redis","slug":"args-should-have-the-same-number-of-keys-and-vals","errorCode":null,"errorMessage":"args should have the same number of keys and vals","messagePattern":"args should have the same number of keys and vals","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/hscan/hscan.go","lineNumber":78,"sourceCode":"\t\treturn StructValue{}, fmt.Errorf(\"redis.Scan(non-pointer %T)\", dst)\n\t}\n\n\tv = v.Elem()\n\tif v.Kind() != reflect.Struct {\n\t\treturn StructValue{}, fmt.Errorf(\"redis.Scan(non-struct %T)\", dst)\n\t}\n\n\treturn StructValue{\n\t\tspec:  globalStructMap.get(v.Type()),\n\t\tvalue: v,\n\t}, nil\n}\n\n// Scan scans the results from a key-value Redis map result set to a destination struct.\n// The Redis keys are matched to the struct's field with the `redis` tag.\nfunc Scan(dst interface{}, keys []interface{}, vals []interface{}) error {\n\tif len(keys) != len(vals) {\n\t\treturn errors.New(\"args should have the same number of keys and vals\")\n\t}\n\n\tstrct, err := Struct(dst)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Iterate through the (key, value) sequence.\n\tfor i := 0; i < len(vals); i++ {\n\t\tkey, ok := keys[i].(string)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tval, ok := vals[i].(string)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/internal/hscan/hscan.go#L60-L96","documentation":"internal/hscan.Scan returns this when the keys and vals slices passed to it have different lengths (hscan.go:77-78). HSCAN/MGET-style key/value pairs must be balanced; a mismatch implies a malformed result set or a caller bug. The error surfaces to users via the exported redis.Scan / ScanStruct helpers.","triggerScenarios":"Calling redis.Scan(&dst, keys, vals) (or HSCAN-driven ScanStruct) where len(keys) != len(vals) — e.g. truncating one slice, mismatched parallel slices, or a corrupted HSCAN reply.","commonSituations":"Manually splitting an HSCAN result into keys/vals arrays and getting the slicing wrong, or a custom deserializer feeding unbalanced slices.","solutions":["Ensure len(keys) == len(vals) before calling Scan.","Build keys/vals from the alternating HSCAN slice with a stride of 2 so they cannot diverge.","Prefer redis.NewScanCmd / ScanStruct over manual slicing."],"exampleFix":"// before\nredis.Scan(&dst, keys[:n], vals) // length mismatch\n// after\nn := len(res) / 2\nkeys := make([]interface{}, n)\nvals := make([]interface{}, n)\nfor i := 0; i < n; i++ {\n    keys[i] = res[i*2]\n    vals[i] = res[i*2+1]\n}\nredis.Scan(&dst, keys, vals)","handlingStrategy":"validation","validationCode":"if len(keys) != len(vals) {\n    return fmt.Errorf(\"keys/vals length mismatch: %d vs %d\", len(keys), len(vals))\n}\nreturn redis.Scan(&dst, keys, vals)","typeGuard":"func balancedKV(keys, vals []interface{}) bool {\n    return len(keys) == len(vals)\n}","tryCatchPattern":"if err := redis.Scan(&dst, keys, vals); err != nil {\n    if strings.Contains(err.Error(), \"same number of keys and vals\") {\n        // re-derive balanced slices from the alternating result\n    }\n}","preventionTips":["Derive keys/vals from the alternating HSCAN slice with stride 2.","Prefer redis.ScanStruct / NewScanCmd over manual slicing."],"tags":["hscan","scan","validation","struct-scanning"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}