hibiken/asynq · error
provided context must have a deadline
Error message
provided context must have a deadline
What it means
Semaphore.Acquire in x/rate implements a Redis-based counting semaphore that relies on the task's deadline to expire stale tokens if a worker crashes without calling Release. It therefore requires the passed context to carry a deadline; a deadline-less context cannot guarantee token reclamation, so Acquire refuses it.
Solutions
- Derive the context from the task context with context.WithDeadline(parent, deadline) before Acquire.
- Ensure you call Acquire from within a task handler using the ctx passed to it (asynq sets deadlines from the task's timeout/deadline).
- If acquiring outside a task, explicitly set a sensible deadline matching your maximum processing time.
- Set a per-task Timeout in the asynq task options so the handler context has a deadline.
Example fix
// before ok, err := sem.Acquire(context.Background()) // after ctx, cancel := context.WithTimeout(taskCtx, 30*time.Second) defer cancel() ok, err := sem.Acquire(ctx)
Defensive patterns
Strategy: validation
Validate before calling
if _, ok := ctx.Deadline(); !ok {
return errors.New("semaphore.Acquire requires a context with deadline")
} Try / catch
ok, err := sem.Acquire(ctx)
if err != nil {
if strings.Contains(err.Error(), "deadline") {
return fmt.Errorf("acquire: %w", err)
}
return err
} Prevention
- Always call Acquire with the task-handler context (asynq sets deadlines from task options).
- Set a Timeout on every task (asynq.Timeout option) so handler contexts always have deadlines.
- In non-task code, wrap with context.WithTimeout before Acquire.
- Never pass context.Background()/TODO() to Acquire.
When it happens
Trigger: Calling sem.Acquire(ctx) with context.Background(), context.TODO(), or any context created via context.WithCancel/WithoutCancel that lost the deadline; calling Acquire outside a task handler where asynq's deadline is not set.
Common situations: Testing the semaphore with context.Background(); reusing the context after wrapping with context.WithoutCancel; acquiring a rate-limit token in code that runs outside asynq's task processing pipeline.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- provided context is missing task ID value
- redis command failed
- batch enqueue does not support group tasks
- batch enqueue does not support unique tasks
- redis connection is shared so the Inspector can't be closed…
AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07).
Data as JSON: /api/errors/8a1f0aa2d0e0bcb1.
Report an issue: GitHub.
Appendix: source
Thrown at x/rate/semaphore.go:71
if (count < tonumber(ARGV[1])) then
redis.call("ZADD", KEYS[1], ARGV[3], ARGV[4])
return 'true'
else
return 'false'
end
`)
// Acquire attempts to acquire a token from the semaphore.
// - Returns (true, nil), iff semaphore key exists and current value is less than maxTokens
// - Returns (false, nil) when token cannot be acquired
// - Returns (false, error) otherwise
//
// The context.Context passed to Acquire must have a deadline set,
// this ensures that token is released if the job goroutine crashes and does not call Release.
func (s *Semaphore) Acquire(ctx context.Context) (bool, error) {
d, ok := ctx.Deadline()
if !ok {
return false, fmt.Errorf("provided context must have a deadline")
}
taskID, ok := asynqcontext.GetTaskID(ctx)
if !ok {
return false, fmt.Errorf("provided context is missing task ID value")
}
return acquireCmd.Run(ctx, s.rc,
[]string{semaphoreKey(s.scope)},
s.maxTokens,
time.Now().Unix(),
d.Unix(),
taskID,
).Bool()
}
// Release will release the token on the counting semaphore.
func (s *Semaphore) Release(ctx context.Context) error {View on GitHub (pinned to d135f1439b)