{"record":{"id":"812eda3a5fffafb5","repo":"hibiken/asynq","slug":"task-id-conflicts-with-another-task-812eda","errorCode":null,"errorMessage":"task id conflicts with another task","messagePattern":"task id conflicts with another task","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/errors/errors.go","lineNumber":176,"sourceCode":"\tif e.Code == Unspecified {\n\t\treturn CanonicalCode(e.Err)\n\t}\n\treturn e.Code\n}\n\n/******************************************\n    Domain Specific Error Types & Values\n*******************************************/\n\nvar (\n\t// ErrNoProcessableTask indicates that there are no tasks ready to be processed.\n\tErrNoProcessableTask = errors.New(\"no tasks are ready for processing\")\n\n\t// ErrDuplicateTask indicates that another task with the same unique key holds the uniqueness lock.\n\tErrDuplicateTask = errors.New(\"task already exists\")\n\n\t// ErrTaskIdConflict indicates that another task with the same task ID already exist\n\tErrTaskIdConflict = errors.New(\"task id conflicts with another task\")\n)\n\n// TaskNotFoundError indicates that a task with the given ID does not exist\n// in the given queue.\ntype TaskNotFoundError struct {\n\tQueue string // queue name\n\tID    string // task id\n}\n\nfunc (e *TaskNotFoundError) Error() string {\n\treturn fmt.Sprintf(\"cannot find task with id=%s in queue %q\", e.ID, e.Queue)\n}\n\n// IsTaskNotFound reports whether any error in err's chain is of type TaskNotFoundError.\nfunc IsTaskNotFound(err error) bool {\n\tvar target *TaskNotFoundError\n\treturn As(err, &target)\n}","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/hibiken/asynq/blob/d135f1439bee74e989b7f9b41ecd542cc87f024a/internal/errors/errors.go#L158-L194","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Don't set an explicit ID (omit the WithTaskID option) and let asynq generate a unique one, unless you specifically need deterministic IDs.","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.","If the ID conflict is expected, treat it as success/idempotency: check errors.Is(err, asynq.ErrTaskIdConflict) and continue.","Clear the conflicting old task (delete/ archive it via Inspector) before enqueueing with the same ID."],"exampleFix":"// before\nif err := client.Enqueue(task, asynq.WithTaskID(orderID)); err != nil {\n    return err // fails with ErrTaskIdConflict on re-runs\n}\n// after\nif err := client.Enqueue(task, asynq.WithTaskID(orderID)); err != nil {\n    if errors.Is(err, asynq.ErrTaskIdConflict) {\n        return nil // task with this ID already queued; idempotent no-op\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"info := inspector.GetTaskInfo(queue, taskID); if info != nil && info.State == asynq.TaskStatePending { /* skip enqueue or use different ID */ }","typeGuard":"func isTaskIDConflict(err error) bool { return errors.Is(err, asynq.ErrTaskIdConflict) }","tryCatchPattern":"if err := client.Enqueue(t, asynq.WithTaskID(id)); err != nil {\n    if errors.Is(err, asynq.ErrTaskIdConflict) {\n        return nil // already enqueued; idempotent\n    }\n    return fmt.Errorf(\"enqueue %s: %w\", id, err)\n}","preventionTips":["Avoid WithTaskID unless deterministic IDs are a hard requirement.","Derive task IDs from truly unique keys (UUIDs) when explicit IDs are needed.","Check for existing tasks via Inspector before re-enqueueing with a fixed ID.","Treat ErrTaskIdConflict as a success signal in idempotent enqueue paths."],"tags":["go","redis","enqueue","duplicate-task-id","idempotency"],"backgroundTag":"file-already-exists","analyzedSha":"d135f1439bee74e989b7f9b41ecd542cc87f024a","analyzedAt":"2026-09-07T19:02:34.660Z","contentChangedAt":"2026-09-07T19:02:34.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}