hibiken/asynq · warning
task already exists
Error message
task already exists
What it means
Internal errors.ErrDuplicateTask (internal/errors/errors.go) indicates another task with the same unique key holds the uniqueness lock, so enqueueing was rejected by the broker. It is the internal counterpart of the public asynq.ErrDuplicateTask, which the client produces by wrapping this value.
Solutions
- Catch the wrapped asynq.ErrDuplicateTask at the client layer and treat as a benign skip.
- Reduce uniqueTTL or wait for expiry before re-enqueueing.
- Vary the payload or add a nonce so the unique key differs.
- Clear the lock by deleting the unique key in Redis if you must enqueue immediately.
Example fix
// before
return client.Enqueue(task, asynq.Unique(ttl))
// after
_, err := client.Enqueue(task, asynq.Unique(ttl))
if errors.Is(err, asynq.ErrDuplicateTask) {
return nil // already queued
}
return err Defensive patterns
Strategy: type-guard
Validate before calling
// Client-side duplicate guard before hitting Redis:
if seen.Load(uniqueKey) { return ErrRecentlyEnqueued } Type guard
func isDuplicate(err error) bool { return errors.Is(err, errors.ErrDuplicateTask) || errors.Is(err, asynq.ErrDuplicateTask) } Try / catch
err := enqueueWithUnique(task, ttl)
if isDuplicate(err) { return nil }
if err != nil { return err } Prevention
- Route all unique enqueues through one helper with uniform error handling
- Track unique TTLs in application metrics to tune them
- Add idempotency keys to payloads so the unique key reflects true business identity
When it happens
Trigger: Enqueueing with a Unique option while a task with the identical unique key (derived from type, payload, queue, and uniqueTTL) is still locked; used by rdb enqueue paths and surfaced through EnqueueContext/EnqueueUnique/AddToGroupUnique.
Common situations: Same as the public variant: fast periodic producers within TTL, duplicate retries after ambiguous network failures, and multiple producers racing on the same job.
Related errors
- task already exists
- task id conflicts with another task
- task ID conflicts with another task
- task ID cannot be empty
- Unique TTL cannot be less than 1s
AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07).
Data as JSON: /api/errors/63bce5b11f91604d.
Report an issue: GitHub.
Appendix: source
Thrown at internal/errors/errors.go:173
if !ok {
return Unspecified
}
if e.Code == Unspecified {
return CanonicalCode(e.Err)
}
return e.Code
}
/******************************************
Domain Specific Error Types & Values
*******************************************/
var (
// ErrNoProcessableTask indicates that there are no tasks ready to be processed.
ErrNoProcessableTask = errors.New("no tasks are ready for processing")
// ErrDuplicateTask indicates that another task with the same unique key holds the uniqueness lock.
ErrDuplicateTask = errors.New("task already exists")
// ErrTaskIdConflict indicates that another task with the same task ID already exist
ErrTaskIdConflict = errors.New("task id conflicts with another task")
)
// TaskNotFoundError indicates that a task with the given ID does not exist
// in the given queue.
type TaskNotFoundError struct {
Queue string // queue name
ID string // task id
}
func (e *TaskNotFoundError) Error() string {
return fmt.Sprintf("cannot find task with id=%s in queue %q", e.ID, e.Queue)
}
// IsTaskNotFound reports whether any error in err's chain is of type TaskNotFoundError.
func IsTaskNotFound(err error) bool {View on GitHub (pinned to d135f1439b)