hibiken/asynq · critical
asynq: unsupported RedisConnOpt type %T
Error message
asynq: unsupported RedisConnOpt type %T
What it means
asynq.NewClient panics when the provided RedisConnOpt's MakeRedisClient() method does not return a value implementing redis.UniversalClient (go-redis). NewClient only accepts connection options whose underlying client is a go-redis UniversalClient; any custom or foreign RedisConnOpt implementation triggers this panic immediately at construction time.
Solutions
- Use one of the built-in options: asynq.RedisClientOpt, RedisClusterClientOpt, or RedisFailoverClientOpt
- If using a custom RedisConnOpt, make MakeRedisClient() return a *redis.Client / *redis.ClusterClient / *redis.UniversalClient that satisfies redis.UniversalClient
- If you already hold a go-redis client, use asynq.NewClientFromRedisClient instead of NewClient
- Check the concrete type returned by MakeRedisClient with %T and confirm it implements redis.UniversalClient
Example fix
// before
conn := myCustomRedisWrapper{addr: ":6379"} // MakeRedisClient returns the wrapper
client := asynq.NewClient(conn)
// after
client := asynq.NewClient(asynq.RedisClientOpt{Addr: ":6379"})
// or, with an existing go-redis client:
client := asynq.NewClientFromRedisClient(rdb) Defensive patterns
Strategy: validation
Validate before calling
opt := asynq.RedisClientOpt{Addr: ":6379"}
if _, ok := opt.MakeRedisClient().(redis.UniversalClient); !ok {
log.Fatal("RedisConnOpt does not produce a go-redis UniversalClient")
}
client := asynq.NewClient(opt) Type guard
func isValidRedisConnOpt(r asynq.RedisConnOpt) bool {
_, ok := r.MakeRedisClient().(redis.UniversalClient)
return ok
} Prevention
- Prefer built-in RedisClientOpt / RedisClusterClientOpt / RedisFailoverClientOpt over custom implementations
- Use NewClientFromRedisClient when you already have a go-redis client
- Keep go-redis version aligned with the version asynq was built against
- Smoke-test Redis construction in an init/unit test so the panic surfaces at CI, not in production startup
When it happens
Trigger: Passing a RedisConnOpt whose MakeRedisClient() returns nil, a raw *redis.Client typed as something else, or a fully custom type that does not implement the interface; constructing asynq.NewClient with a hand-rolled adapter struct; passing a zero-value custom conn option.
Common situations: Wrapping a redis cluster/sentinel setup in a custom config struct instead of asynq.RedisClientOpt/RedisClusterClientOpt/RedisFailoverClientOpt; upgrading go-redis versions so the UniversalClient interface no longer matches; typos where a config object that merely resembles RedisConnOpt is passed in.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- inspeq: unsupported RedisConnOpt type %T
- asynq: unsupported RedisConnOpt type %T
- asynq: unsupported RedisConnOpt type %T
- asynq: could not parse redis uri
- asynq: unsupported uri scheme
AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07).
Data as JSON: /api/errors/a0ec2de9a467e64d.
Report an issue: GitHub.
Appendix: source
Thrown at client.go:39
// A Client is responsible for scheduling tasks.
//
// A Client is used to register tasks that should be processed
// immediately or some time in the future.
//
// Clients are safe for concurrent use by multiple goroutines.
type Client struct {
broker base.Broker
// When a Client has been created with an existing Redis connection, we do
// not want to close it.
sharedConnection bool
}
// NewClient returns a new Client instance given a redis connection option.
func NewClient(r RedisConnOpt) *Client {
redisClient, ok := r.MakeRedisClient().(redis.UniversalClient)
if !ok {
panic(fmt.Sprintf("asynq: unsupported RedisConnOpt type %T", r))
}
client := NewClientFromRedisClient(redisClient)
client.sharedConnection = false
return client
}
// NewClientFromRedisClient returns a new instance of Client given a redis.UniversalClient
// Warning: The underlying redis connection pool will not be closed by Asynq, you are responsible for closing it.
func NewClientFromRedisClient(c redis.UniversalClient) *Client {
return &Client{broker: rdb.NewRDB(c), sharedConnection: true}
}
type OptionType int
const (
MaxRetryOpt OptionType = iota
QueueOpt
TimeoutOptView on GitHub (pinned to d135f1439b)