{"record":{"id":"4e8d577ae68ab9c1","repo":"hibiken/asynq","slug":"provided-context-is-missing-task-id-value","errorCode":null,"errorMessage":"provided context is missing task ID value","messagePattern":"provided context is missing task ID value","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"x/rate/semaphore.go","lineNumber":76,"sourceCode":"end\n`)\n\n// Acquire attempts to acquire a token from the semaphore.\n// - Returns (true, nil), iff semaphore key exists and current value is less than maxTokens\n// - Returns (false, nil) when token cannot be acquired\n// - Returns (false, error) otherwise\n//\n// The context.Context passed to Acquire must have a deadline set,\n// this ensures that token is released if the job goroutine crashes and does not call Release.\nfunc (s *Semaphore) Acquire(ctx context.Context) (bool, error) {\n\td, ok := ctx.Deadline()\n\tif !ok {\n\t\treturn false, fmt.Errorf(\"provided context must have a deadline\")\n\t}\n\n\ttaskID, ok := asynqcontext.GetTaskID(ctx)\n\tif !ok {\n\t\treturn false, fmt.Errorf(\"provided context is missing task ID value\")\n\t}\n\n\treturn acquireCmd.Run(ctx, s.rc,\n\t\t[]string{semaphoreKey(s.scope)},\n\t\ts.maxTokens,\n\t\ttime.Now().Unix(),\n\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","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/hibiken/asynq/blob/d135f1439bee74e989b7f9b41ecd542cc87f024a/x/rate/semaphore.go#L58-L94","documentation":"Semaphore.Acquire reads the current task's ID from the context (stored by asynq's task-processing machinery) so the acquired token is keyed to the task. If the context lacks the task ID value, Acquire cannot record ownership and fails with this error.","triggerScenarios":"Calling sem.Acquire(ctx) with a context that did not originate from an asynq task handler (no task ID stored via asynqcontext), e.g. context.Background() or a manually created context.","commonSituations":"Using x/rate.Semaphore outside a task handler; wrapping/re-creating the context in a way that drops the stored task ID value; unit tests calling Acquire with a plain context.","solutions":["Call Acquire with the ctx given to your asynq task handler (func(ctx context.Context, t *asynq.Task) error).","In tests, store a task ID in the context the same way asynq does (via the asynq context helpers) before Acquire.","Avoid context.WithoutCancel-style wrappers that strip context values; derive from the original task context.","If you need the semaphore outside tasks, store a task ID in the context manually matching asynqcontext's key."],"exampleFix":"// before\nok, err := sem.Acquire(context.Background())\n// after\nfunc myHandler(ctx context.Context, t *asynq.Task) error {\n    ok, err := sem.Acquire(ctx) // ctx carries the task ID set by asynq\n    ...\n}","handlingStrategy":"validation","validationCode":"if _, ok := asynqcontext.GetTaskID(ctx); !ok {\n    return errors.New(\"semaphore.Acquire requires an asynq task context with task ID\")\n}","typeGuard":null,"tryCatchPattern":"ok, err := sem.Acquire(ctx)\nif err != nil {\n    return fmt.Errorf(\"acquire failed: %w\", err)\n}","preventionTips":["Use the semaphore only inside asynq task handlers where the task ID is in the context.","Derive contexts (WithTimeout/WithValue) from the task context, never from Background.","In tests, inject a task ID into the context using the same asynq context helpers."],"tags":["go","asynq","rate-limiting","context","semaphore"],"backgroundTag":"missing-required-argument","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"}