{"record":{"id":"fe804a0d296a1571","repo":"bytebase/bytebase","slug":"expect-redis-clusterclient-but-get-t","errorCode":null,"errorMessage":"expect *redis.ClusterClient, but get %T","messagePattern":"expect \\*redis\\.ClusterClient, but get %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/plugin/db/redis/redis.go","lineNumber":274,"sourceCode":"\t\t\tfor _, input := range inputs {\n\t\t\t\tcmd := p.Do(ctx, input...)\n\t\t\t\tcmds = append(cmds, cmd)\n\t\t\t}\n\t\t\treturn nil\n\t\t}); err != nil && err != redis.Nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor i, cmd := range cmds {\n\t\t\tsetQueryResultRows(results[i], cmd, queryContext.MaximumSQLResultSize)\n\t\t\tresults[i].Latency = durationpb.New(time.Since(startTime))\n\t\t\tresults[i].RowsCount = int64(len(results[i].Rows))\n\t\t}\n\n\tcase v1pb.QueryOption_ALL_NODES:\n\t\tcluster, ok := d.rdb.(*redis.ClusterClient)\n\t\tif !ok {\n\t\t\treturn nil, errors.Errorf(\"expect *redis.ClusterClient, but get %T\", d.rdb)\n\t\t}\n\n\t\tvar cmdss [][]*redis.Cmd\n\t\tcmdsChan := make(chan []*redis.Cmd)\n\t\tstopChan := make(chan struct{})\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase cmds := <-cmdsChan:\n\t\t\t\t\tcmdss = append(cmdss, cmds)\n\t\t\t\tcase <-stopChan:\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\n\t\terr := cluster.ForEachShard(ctx, func(ctx context.Context, client *redis.Client) error {\n\t\t\tvar cmds []*redis.Cmd","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/plugin/db/redis/redis.go#L256-L292","documentation":"With QueryOption_ALL_NODES, QueryConn must fan a command out to every node of a Redis cluster, so it asserts d.rdb is a *redis.ClusterClient. If the underlying client is a plain Client (standalone/sentinel) instead, the type assertion fails and the error names the actual concrete type received.","triggerScenarios":"Executing a query with QueryOption_ALL_NODES against an instance configured as STANDALONE or SENTINEL — the rdb field holds redis.Client/SentinelClient, not redis.ClusterClient.","commonSituations":"Users selecting the \"run on all nodes\" option for a non-cluster Redis; instance type misconfigured (configured as standalone but actually cluster, or vice versa).","solutions":["Don't use the ALL_NODES query option unless the instance is a Redis cluster.","Change the instance's redisType to CLUSTER if it truly is a cluster so d.rdb is a ClusterClient.","Re-run the query with the default (single-node) query option instead."],"exampleFix":"// before: ALL_NODES on a standalone instance\nqueryContext.Option.QueryType = v1pb.QueryOption_ALL_NODES\n// after: use default/single-node execution\nqueryContext.Option.QueryType = v1pb.QueryOption_BASIC","handlingStrategy":"type-guard","validationCode":"if qCtx.Option.QueryType == v1pb.QueryOption_ALL_NODES && cfg.DataSource.GetRedisType() != storepb.DataSource_REDIS_TYPE_CLUSTER {\n\tlog.Fatal(\"ALL_NODES requires a Redis cluster instance\")\n}","typeGuard":"cluster, ok := d.rdb.(*redis.ClusterClient)\nif !ok {\n\treturn nil, fmt.Errorf(\"ALL_NODES unsupported: client is %T\", d.rdb)\n}","tryCatchPattern":"results, err := d.QueryConn(ctx, conn, stmt, qCtx)\nif err != nil && strings.Contains(err.Error(), \"expect *redis.ClusterClient\") {\n\t// fall back to single-node query\n}","preventionTips":["Only enable ALL_NODES for instances whose redisType is CLUSTER.","Verify instance type in settings before cluster-only operations.","Wrap with a type assertion when writing plugin-level code."],"tags":["redis","cluster","type-mismatch","query-option"],"backgroundTag":"incompatible-source-type","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}