hibiken/asynq · critical

asynq: unsupported RedisConnOpt type %T

Error message

asynq: unsupported RedisConnOpt type %T

What it means

asynq.NewScheduler panics when the provided RedisConnOpt's MakeRedisClient() does not return a redis.UniversalClient. Like NewClient and NewServer, the scheduler only supports connection options backed by a go-redis UniversalClient, and an incompatible implementation causes an immediate panic at construction time.

Solutions

  1. Use built-in options: asynq.RedisClientOpt, RedisClusterClientOpt, or RedisFailoverClientOpt
  2. If you already hold a go-redis client, build the Scheduler via NewScheduler with a valid option or adapt using an asynq-supported client constructor
  3. Make the custom RedisConnOpt's MakeRedisClient return a redis.UniversalClient-implementing value
  4. Assert compatibility in a small startup check: if _, ok := r.MakeRedisClient().(redis.UniversalClient); !ok { fail fast with a clear message }

Example fix

// before
sched := asynq.NewScheduler(brokenOpt{}, nil) // panics

// after
sched, err := asynq.NewScheduler(asynq.RedisClientOpt{Addr: ":6379"}, nil)
if err != nil { log.Fatal(err) }
Defensive patterns

Strategy: validation

Validate before calling

opt := asynq.RedisClientOpt{Addr: ":6379"}
if _, ok := opt.MakeRedisClient().(redis.UniversalClient); !ok {
    log.Fatal("scheduler RedisConnOpt incompatible")
}
sched := asynq.NewScheduler(opt, nil)

Type guard

func schedConnOptValid(r asynq.RedisConnOpt) bool {
    _, ok := r.MakeRedisClient().(redis.UniversalClient)
    return ok
}

Prevention

When it happens

Trigger: Passing a custom RedisConnOpt whose MakeRedisClient returns nil or a non-go-redis client type; using a wrapper/adapter struct; passing an option type from an incompatible go-redis major version.

Common situations: Building the scheduler with the same (broken) custom conn opt already used elsewhere; config-driven construction where an unvalidated config value produces a foreign conn-opt type; mixing asynq versions with different go-redis dependencies.

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


AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07). Data as JSON: /api/errors/1f3b25abec3c5e39. Report an issue: GitHub.

Appendix: source

Thrown at scheduler.go:58

	// guards idmap
	mu sync.Mutex
	// idmap maps Scheduler's entry ID to cron.EntryID
	// to avoid using cron.EntryID as the public API of
	// the Scheduler.
	idmap map[string]cron.EntryID
}

const defaultHeartbeatInterval = 10 * time.Second

// NewScheduler returns a new Scheduler instance given the redis connection option.
// The parameter opts is optional, defaults will be used if opts is set to nil
func NewScheduler(r RedisConnOpt, opts *SchedulerOpts) *Scheduler {
	scheduler := newScheduler(opts)

	redisClient, ok := r.MakeRedisClient().(redis.UniversalClient)
	if !ok {
		panic(fmt.Sprintf("asynq: unsupported RedisConnOpt type %T", r))
	}

	rdb := rdb.NewRDB(redisClient)

	scheduler.rdb = rdb
	scheduler.client = &Client{broker: rdb, sharedConnection: false}

	return scheduler
}

// NewSchedulerFromRedisClient returns a new instance of Scheduler given a redis.UniversalClient
// The parameter opts is optional, defaults will be used if opts is set to nil.
// Warning: The underlying redis connection pool will not be closed by Asynq, you are responsible for closing it.
func NewSchedulerFromRedisClient(c redis.UniversalClient, opts *SchedulerOpts) *Scheduler {
	scheduler := newScheduler(opts)

	scheduler.rdb = rdb.NewRDB(c)
	scheduler.client = NewClientFromRedisClient(c)

View on GitHub (pinned to d135f1439b)