hibiken/asynq · warning

queue is already paused

Error message

queue %q is already paused

What it means

Returned by RDB.Pause when the target queue is already in the paused state. Pause uses SetNX on the paused-key; if the key already exists, SetNX fails and this error is raised. It is an idempotency/state guard, not a network or data failure.

Solutions

  1. Check IsPaused(queue) before calling Pause
  2. Treat this error as a no-op success if the desired state is 'paused'
  3. Serialize pause/unpause operations through one controller
  4. Use a retry loop that tolerates this specific error

Example fix

// before
if err := inspector.Pause(q); err != nil {
    return err
}
// after
if err := inspector.Pause(q); err != nil && !strings.Contains(err.Error(), "is already paused") {
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

paused, err := inspector.IsPaused(q)
if err != nil { return err }
if paused { return nil } // nothing to do

Try / catch

if err := inspector.Pause(q); err != nil && !strings.Contains(err.Error(), "already paused") {
    return err
} // treat already-paused as success

Prevention

When it happens

Trigger: Calling inspector.Pause(queue) twice, or calling Pause on a queue paused by another process/instance without an intervening Unpause.

Common situations: Multiple schedulers or ops scripts pausing the same queue concurrently; re-running an idempotent deployment script that pauses queues; retry logic that repeats Pause after a timeout.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at internal/rdb/inspect.go:2082

		}
		e, err := base.DecodeSchedulerEnqueueEvent([]byte(data))
		if err != nil {
			return nil, err
		}
		events = append(events, e)
	}
	return events, nil
}

// Pause pauses processing of tasks from the given queue.
func (r *RDB) Pause(qname string) error {
	key := base.PausedKey(qname)
	ok, err := r.client.SetNX(context.Background(), key, r.clock.Now().Unix(), 0).Result()
	if err != nil {
		return err
	}
	if !ok {
		return fmt.Errorf("queue %q is already paused", qname)
	}
	return nil
}

// Unpause resumes processing of tasks from the given queue.
func (r *RDB) Unpause(qname string) error {
	key := base.PausedKey(qname)
	deleted, err := r.client.Del(context.Background(), key).Result()
	if err != nil {
		return err
	}
	if deleted == 0 {
		return fmt.Errorf("queue %q is not paused", qname)
	}
	return nil
}

// ClusterKeySlot returns an integer identifying the hash slot the given queue hashes to.

View on GitHub (pinned to d135f1439b)