hibiken/asynq · error

task id conflicts with another task

Error message

task id conflicts with another task

What it means

ErrTaskIdConflict indicates that another task with the same task ID already exists in the queue. Asynq lets callers supply an explicit task ID; the broker enforces that IDs are unique among pending/scheduled tasks by detecting a zero-return from the underlying Redis operation (internal/rdb/rdb.go:137 maps n==0 to this error via errors.AlreadyExists). It is returned by the enqueue-family APIs: EnqueueContext, Enqueue, EnqueueUnique, AddToGroup, AddToGroupUnique, and Schedule.

Solutions

  1. Don't set an explicit ID (omit the WithTaskID option) and let asynq generate a unique one, unless you specifically need deterministic IDs.
  2. Before enqueueing, check whether a task with that ID already exists (e.g. via inspector.ListPendingTasks / inspector.GetTaskInfo) and skip or update instead of enqueueing.
  3. If the ID conflict is expected, treat it as success/idempotency: check errors.Is(err, asynq.ErrTaskIdConflict) and continue.
  4. Clear the conflicting old task (delete/ archive it via Inspector) before enqueueing with the same ID.

Example fix

// before
if err := client.Enqueue(task, asynq.WithTaskID(orderID)); err != nil {
    return err // fails with ErrTaskIdConflict on re-runs
}
// after
if err := client.Enqueue(task, asynq.WithTaskID(orderID)); err != nil {
    if errors.Is(err, asynq.ErrTaskIdConflict) {
        return nil // task with this ID already queued; idempotent no-op
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

info := inspector.GetTaskInfo(queue, taskID); if info != nil && info.State == asynq.TaskStatePending { /* skip enqueue or use different ID */ }

Type guard

func isTaskIDConflict(err error) bool { return errors.Is(err, asynq.ErrTaskIdConflict) }

Try / catch

if err := client.Enqueue(t, asynq.WithTaskID(id)); err != nil {
    if errors.Is(err, asynq.ErrTaskIdConflict) {
        return nil // already enqueued; idempotent
    }
    return fmt.Errorf("enqueue %s: %w", id, err)
}

Prevention

When it happens

Trigger: Calling Enqueue/EnqueueUnique/Schedule/AddToGroup/AddToGroupUnique with a task whose Option (WithTaskID) sets an ID that already matches an existing pending, scheduled, or aggregated task in the same queue.

Common situations: Re-enqueuing the same task after a failure without changing or clearing the explicit ID; client code deriving task IDs from user-supplied keys (order IDs, request IDs) that can repeat; retrying an enqueue after a network blip where the first attempt actually succeeded; idempotency schemes that treat explicit IDs like unique keys without realizing uniqueness only applies to pending tasks.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at internal/errors/errors.go:176

	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 {
	var target *TaskNotFoundError
	return As(err, &target)
}

View on GitHub (pinned to d135f1439b)