hibiken/asynq · warning
task already exists
Error message
task already exists
What it means
ErrDuplicateTask (errors.ErrDuplicateTask in package errors, wrapped as asynq.ErrDuplicateTask in client.go) indicates the task could not be enqueued because a task with the same uniqueness key already exists. It only applies to tasks enqueued with a Unique option (Unique/uniqueTTL). The client translates the internal errors.ErrDuplicateTask from the broker into the public sentinel via errors.Is.
Solutions
- Check errors.Is(err, asynq.ErrDuplicateTask) and treat it as success/expected when the task is already queued.
- Shorten the Unique TTL so locks expire sooner, or clear the uniqueness lock by waiting for TTL expiry.
- Include a nonce or ID in the task payload to differentiate genuinely distinct tasks.
- Use TaskID option instead if you want conflict semantics rather than uniqueness semantics.
Example fix
// before
_, err := client.Enqueue(task, asynq.Unique(24*time.Hour))
if err != nil { log.Fatal(err) }
// after
_, err := client.Enqueue(task, asynq.Unique(24*time.Hour))
if errors.Is(err, asynq.ErrDuplicateTask) {
log.Println("task already enqueued; skipping")
return
} Defensive patterns
Strategy: try-catch
Validate before calling
// Nothing to pre-validate (uniqueness is server-side); optionally track TTLs locally:
if time.Since(lastEnqueued[key]) < ttl { return ErrAlreadyQueued } Type guard
func isDuplicateTask(err error) bool { return errors.Is(err, asynq.ErrDuplicateTask) } Try / catch
if _, err := client.Enqueue(task, asynq.Unique(ttl)); err != nil {
if isDuplicateTask(err) { return nil }
return fmt.Errorf("enqueue: %w", err)
} Prevention
- Wrap Unique enqueues with a dedicated helper that treats duplicates as success
- Keep uniqueTTL comfortably longer than the task's processing duration
- Include enough identity (IDs, timestamps) in payloads to disambiguate
- Log duplicate occurrences to detect over-aggressive producers
When it happens
Trigger: Calling client.Enqueue with asynq.Unique(ttl) (or EnqueueUnique/AddToGroupUnique) while an identical task (same type, payload, queue, and unique key) is still within its uniqueness TTL; a prior identical enqueue has not expired yet.
Common situations: Periodic job schedulers firing faster than the Unique TTL; retrying an enqueue after a timeout when the first attempt actually succeeded and set the unique lock; multiple workers/producers racing to enqueue the same deduplicated 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/b4d286ee48af0a00.
Report an issue: GitHub.
Appendix: source
Thrown at client.go:248
// Alternatively, NewTaskWithHeaders can be used to create a task with headers
// directly, which may be preferable when headers are an intrinsic part of the
// task definition rather than enqueue-time configuration.
func Header(key, value string) Option {
return headerOption{key, value}
}
func (h headerOption) String() string {
var bytes []byte
bytes, _ = json.Marshal(h)
return fmt.Sprintf("Header(%s)", bytes)
}
func (h headerOption) Type() OptionType { return HeaderOpt }
func (h headerOption) Value() interface{} { return [2]string{h[0], h[1]} }
// ErrDuplicateTask indicates that the given task could not be enqueued since it's a duplicate of another task.
//
// ErrDuplicateTask error only applies to tasks enqueued with a Unique option.
var ErrDuplicateTask = errors.New("task already exists")
// ErrTaskIDConflict indicates that the given task could not be enqueued since its task ID already exists.
//
// ErrTaskIDConflict error only applies to tasks enqueued with a TaskID option.
var ErrTaskIDConflict = errors.New("task ID conflicts with another task")
type option struct {
retry int
queue string
taskID string
timeout time.Duration
deadline time.Time
uniqueTTL time.Duration
processAt time.Time
retention time.Duration
group string
headers map[string]string
}View on GitHub (pinned to d135f1439b)