{"record":{"id":"d3c7b5b20a458a25","repo":"hibiken/asynq","slug":"no-token-found-for-task-q","errorCode":null,"errorMessage":"no token found for task %q","messagePattern":"no token found for task %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"x/rate/semaphore.go","lineNumber":101,"sourceCode":"\t\td.Unix(),\n\t\ttaskID,\n\t).Bool()\n}\n\n// Release will release the token on the counting semaphore.\nfunc (s *Semaphore) Release(ctx context.Context) error {\n\ttaskID, ok := asynqcontext.GetTaskID(ctx)\n\tif !ok {\n\t\treturn fmt.Errorf(\"provided context is missing task ID value\")\n\t}\n\n\tn, err := s.rc.ZRem(ctx, semaphoreKey(s.scope), taskID).Result()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"redis command failed: %w\", err)\n\t}\n\n\tif n == 0 {\n\t\treturn fmt.Errorf(\"no token found for task %q\", taskID)\n\t}\n\n\treturn nil\n}\n\n// Close closes the connection to redis.\nfunc (s *Semaphore) Close() error {\n\treturn s.rc.Close()\n}\n\nfunc semaphoreKey(scope string) string {\n\treturn \"asynq:sema:\" + scope\n}\n","sourceCodeStart":83,"sourceCodeEnd":115,"githubUrl":"https://github.com/hibiken/asynq/blob/d135f1439bee74e989b7f9b41ecd542cc87f024a/x/rate/semaphore.go#L83-L115","documentation":"This error is returned by Semaphore.Release when the Redis ZREM operation against the semaphore's sorted set removes zero members, meaning no token/lease was recorded in Redis for the given taskID. The library treats releasing a task that never acquired (or already released / was evicted from) the semaphore as a caller error rather than silently succeeding, so the caller knows its bookkeeping is off.","triggerScenarios":"Calling Release(taskID) with a taskID that never called Acquire, calling Release twice for the same taskID (the second ZREM deletes nothing), the token expiring or being removed from the sorted set (e.g. lease expiry/cleanup) before Release runs, or using a taskID string that differs from the one passed to Acquire (case/format mismatch).","commonSituations":"Worker code that releases in a deferred cleanup path which can run more than once; retries that re-release an already-released task after a failed job; passing a job ID instead of the acquire-time task ID; Redis flush/restart wiping the semaphore key between Acquire and Release.","solutions":["Ensure Release is called exactly once per successful Acquire, guarding with a bool/sync.Once or a 'released' flag in deferred cleanup","Verify the taskID passed to Release is byte-identical to the one returned by/used in Acquire","Treat the error as idempotent-safe if double-release is expected in your design: log and continue instead of failing the worker","Check for lease expiry or external cleanup of the semaphore key that removes tokens before Release runs"],"exampleFix":"// before\ndefer s.Release(ctx, taskID)\n\n// after\nvar released bool\ndefer func() {\n    if !released {\n        released = true\n        if err := s.Release(ctx, taskID); err != nil {\n            log.Printf(\"semaphore release skipped: %v\", err)\n        }\n    }\n}()","handlingStrategy":"try-catch","validationCode":"// Go has no pre-call check without a round trip; optionally verify the token exists first:\nn, _ := s.rc.ZScore(ctx, semaphoreKey(s.scope), taskID).Result()\nif errors.Is(n, redis.Nil) { return nil /* already released */ }","typeGuard":null,"tryCatchPattern":"if err := sem.Release(ctx, taskID); err != nil {\n    if strings.Contains(err.Error(), \"no token found for task\") {\n        log.Printf(\"task %s already released or expired, ignoring\", taskID)\n        return nil\n    }\n    return err\n}","preventionTips":["Track acquired taskIDs locally and only Release ones you acquired","Use sync.Once or a bool flag so deferred release runs at most once","Keep the exact taskID value from Acquire; do not reformat or substitute job IDs","Decide explicitly whether double-release should be an error or a no-op and handle consistently"],"tags":["redis","rate-limiting","concurrency"],"backgroundTag":"resource-not-found","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"}