{"record":{"id":"b7b6f0e31c4c5bf2","repo":"hibiken/asynq","slug":"task-id-conflicts-with-another-task","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":"client.go","lineNumber":253,"sourceCode":"}\n\nfunc (h headerOption) String() string {\n\tvar bytes []byte\n\tbytes, _ = json.Marshal(h)\n\treturn fmt.Sprintf(\"Header(%s)\", bytes)\n}\nfunc (h headerOption) Type() OptionType   { return HeaderOpt }\nfunc (h headerOption) Value() interface{} { return [2]string{h[0], h[1]} }\n\n// ErrDuplicateTask indicates that the given task could not be enqueued since it's a duplicate of another task.\n//\n// ErrDuplicateTask error only applies to tasks enqueued with a Unique option.\nvar ErrDuplicateTask = errors.New(\"task already exists\")\n\n// ErrTaskIDConflict indicates that the given task could not be enqueued since its task ID already exists.\n//\n// ErrTaskIDConflict error only applies to tasks enqueued with a TaskID option.\nvar ErrTaskIDConflict = errors.New(\"task ID conflicts with another task\")\n\ntype option struct {\n\tretry     int\n\tqueue     string\n\ttaskID    string\n\ttimeout   time.Duration\n\tdeadline  time.Time\n\tuniqueTTL time.Duration\n\tprocessAt time.Time\n\tretention time.Duration\n\tgroup     string\n\theaders   map[string]string\n}\n\n// composeOptions merges user provided options into the default options\n// and returns the composed option.\n// It also validates the user provided options and returns an error if any of\n// the user provided options fail the validations.","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/hibiken/asynq/blob/d135f1439bee74e989b7f9b41ecd542cc87f024a/client.go#L235-L271","documentation":"ErrTaskIDConflict indicates the task could not be enqueued because another task with the same TaskID already exists (in active/pending/scheduled state). It only applies to tasks enqueued with a TaskID option. The client wraps the internal errors.ErrTaskIdConflict into this public sentinel.","triggerScenarios":"Calling client.Enqueue with asynq.TaskID(id) where a task with that exact ID still exists in any queue state (pending, active, scheduled, retry, archived, or within retention).","commonSituations":"Using user/entity IDs as TaskID without allowing for task completion time; re-enqueuing after retention-expiry assumptions are wrong; two producers picking the same deterministic TaskID.","solutions":["Check errors.Is(err, asynq.ErrTaskIDConflict) and skip/requeue later — the task already exists.","Use a unique ID (UUID) per task instead of reusing stable business IDs.","Wait for the existing task to finish and its retention period to expire before reusing the ID.","Shorten the Retention duration on the original task so the ID frees up sooner."],"exampleFix":"// before\n_, err := client.Enqueue(task, asynq.TaskID(userID))\n// after\n_, err := client.Enqueue(task, asynq.TaskID(fmt.Sprintf(\"sync:%s:%d\", userID, time.Now().Unix())))","handlingStrategy":"try-catch","validationCode":"// Check whether an ID-derived task may still exist before enqueue:\nif activeTaskIDs[taskID] { return ErrTaskInProgress }","typeGuard":"func isTaskIDConflict(err error) bool { return errors.Is(err, asynq.ErrTaskIDConflict) }","tryCatchPattern":"if _, err := client.Enqueue(task, asynq.TaskID(id)); err != nil {\n    if isTaskIDConflict(err) { return ErrAlreadySubmitted }\n    return err\n}","preventionTips":["Use UUIDs or include a monotonic sequence in TaskID","Set Retention deliberately — a task ID is blocked until retention expires","Document TaskID semantics for anyone reusing business keys as IDs"],"tags":["redis","task-queue","task-id","conflict"],"backgroundTag":"duplicate-task","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"}