hibiken/asynq · error
rate.NewSemaphore: unsupported RedisConnOpt type %T
Error message
rate.NewSemaphore: unsupported RedisConnOpt type %T
What it means
asynq's x/rate.NewSemaphore panics when the provided RedisConnOpt does not produce a client implementinging redis.UniversalClient via MakeRedisClient(). Only RedisClient, RedisClusterClient, and FailoverClient options are supported. Any other RedisConnOpt implementation is rejected at construction time.
Solutions
- Use a supported option: asynq.NewRedisClientOptimizer or asynq.ParseRedisURI("redis://...") / asynq.RedisClientOpt{Addr: ...}.
- If using a cluster, switch to asynq.RedisClusterClientOpt.
- Verify rco.MakeRedisClient() returns a value implementing redis.UniversalClient before calling NewSemaphore.
- In tests, use a real/miniredis-backed RedisClientOpt instead of a custom mock RedisConnOpt.
Example fix
// before
type myOpt struct{}
func (myOpt) MakeRedisClient() interface{} { return someCustomClient }
sem := rate.NewSemaphore(myOpt{}, "scope", 10)
// after
rco := asynq.RedisClientOpt{Addr: "localhost:6379"}
sem := rate.NewSemaphore(rco, "my-scope", 10) Defensive patterns
Strategy: type-guard
Validate before calling
func isSupportedOpt(rco asynq.RedisConnOpt) bool {
_, ok := rco.MakeRedisClient().(redis.UniversalClient)
return ok
} Type guard
func asUniversalClient(rco asynq.RedisConnOpt) (redis.UniversalClient, bool) {
c, ok := rco.MakeRedisClient().(redis.UniversalClient)
return c, ok
} Try / catch
// panics cannot be caught by try/catch in Go; guard before calling:
if c, ok := asUniversalClient(rco); ok {
_ = rate.NewSemaphore(rco, scope, tokens)
} else {
return fmt.Errorf("unsupported RedisConnOpt %T; use RedisClientOpt/RedisClusterClientOpt/FailoverClientOpt", rco)
} Prevention
- Only use asynq.RedisClientOpt, RedisClusterClientOpt, or FailoverClientOpt with x/rate.
- Use asynq.ParseRedisURI for connection strings to get a supported option.
- Avoid custom RedisConnOpt implementations or mocks with NewSemaphore.
- Write a smoke test that constructs the semaphore with your production connection option.
When it happens
Trigger: Calling rate.NewSemaphore(rco, scope, maxTokens) where rco is a custom RedisConnOpt implementation, a nil option, or a type whose MakeRedisClient() returns something other than a redis.UniversalClient (e.g. a plain redis.Client is fine, but a mock or wrapper is not).
Common situations: Passing a custom/mock RedisConnOpt in tests; using an older or third-party RedisConnOpt implementation; passing a nil or wrongly-typed option after refactoring; assuming any asynq.RedisConnOpt works with the x/rate package.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- redis command failed
- asynq: unsupported RedisConnOpt type %T
- inspeq: unsupported RedisConnOpt type %T
- asynq: unsupported RedisConnOpt type %T
- asynq: unsupported RedisConnOpt type %T
AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07).
Data as JSON: /api/errors/010e7efcf123fb1d.
Report an issue: GitHub.
Appendix: source
Thrown at x/rate/semaphore.go:19
// Package rate contains rate limiting strategies for asynq.Handler(s).
package rate
import (
"context"
"fmt"
"strings"
"time"
"github.com/hibiken/asynq"
asynqcontext "github.com/hibiken/asynq/internal/context"
"github.com/redis/go-redis/v9"
)
// NewSemaphore creates a counting Semaphore for the given scope with the given number of tokens.
func NewSemaphore(rco asynq.RedisConnOpt, scope string, maxTokens int) *Semaphore {
rc, ok := rco.MakeRedisClient().(redis.UniversalClient)
if !ok {
panic(fmt.Sprintf("rate.NewSemaphore: unsupported RedisConnOpt type %T", rco))
}
if maxTokens < 1 {
panic("rate.NewSemaphore: maxTokens cannot be less than 1")
}
if len(strings.TrimSpace(scope)) == 0 {
panic("rate.NewSemaphore: scope should not be empty")
}
return &Semaphore{
rc: rc,
scope: scope,
maxTokens: maxTokens,
}
}
// Semaphore is a distributed counting semaphore which can be used to set maxTokens across multiple asynq servers.View on GitHub (pinned to d135f1439b)