hibiken/asynq · error
redis command failed
Error message
redis command failed: %w
What it means
Semaphore.Release removes the caller's token from the Redis sorted set with ZRem. If the Redis command itself fails (connection error, timeout, read-only replica, cluster error), the error is wrapped as "redis command failed" and returned.
Solutions
- Check Redis availability (redis-cli ping) and fix connection settings in RedisClientOpt.
- Unwrap the %w cause to identify connection-refused vs timeout vs auth errors.
- Add retry with backoff for Release on transient network errors, since failing to release stalls the semaphore slot.
- Ensure the Semaphore and the asynq server use the same reachable Redis instance/database.
Example fix
// before
sem := NewSemaphore(rdb, "scope", 1)
// rdb points at 127.0.0.1:6399 (down)
// after
sem := NewSemaphore(rdb, "scope", 1)
if err := sem.Release(ctx); err != nil {
log.Printf("release failed: %v", err) // inspect wrapped redis cause
} Defensive patterns
Strategy: retry
Validate before calling
if err := rdb.Ping(ctx).Err(); err != nil {
log.Printf("redis unavailable: %v", err)
} Try / catch
if err := sem.Release(ctx); err != nil {
// retry a few times before giving up so the slot isn't leaked
for i := 0; i < 3; i++ {
time.Sleep(100*time.Millisecond)
if err = sem.Release(ctx); err == nil { break }
}
log.Printf("release failed after retries: %v", err)
} Prevention
- Configure RedisClientOpt with sane DialTimeout/ReadTimeout and retries.
- Monitor Redis health; stale tokens are eventually reaped by deadline expiry, but minimize churn.
- Point the Semaphore at the same Redis instance/DB the asynq server uses.
When it happens
Trigger: Calling sem.Release(ctx) while Redis is unreachable, the connection pool is exhausted, the key is in a wrong cluster slot, or Redis returns any command-level error.
Common situations: Redis restart or failover mid-processing; network blips between app and Redis; connecting the Semaphore to a different/replica Redis instance than intended; AUTH required but not configured.
Related errors
- provided context must have a deadline
- provided context is missing task ID value
- redis connection is shared so the Inspector can't be closed…
- failed to get queue names
- failed to get queue info
AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07).
Data as JSON: /api/errors/78be52bf40abf735.
Report an issue: GitHub.
Appendix: source
Thrown at x/rate/semaphore.go:97
return acquireCmd.Run(ctx, s.rc,
[]string{semaphoreKey(s.scope)},
s.maxTokens,
time.Now().Unix(),
d.Unix(),
taskID,
).Bool()
}
// Release will release the token on the counting semaphore.
func (s *Semaphore) Release(ctx context.Context) error {
taskID, ok := asynqcontext.GetTaskID(ctx)
if !ok {
return fmt.Errorf("provided context is missing task ID value")
}
n, err := s.rc.ZRem(ctx, semaphoreKey(s.scope), taskID).Result()
if err != nil {
return fmt.Errorf("redis command failed: %w", err)
}
if n == 0 {
return fmt.Errorf("no token found for task %q", taskID)
}
return nil
}
// Close closes the connection to redis.
func (s *Semaphore) Close() error {
return s.rc.Close()
}
func semaphoreKey(scope string) string {
return "asynq:sema:" + scope
}
View on GitHub (pinned to d135f1439b)