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

  1. Derive the context from the task context with context.WithDeadline(parent, deadline) before Acquire.
  2. Ensure you call Acquire from within a task handler using the ctx passed to it (asynq sets deadlines from the task's timeout/deadline).
  3. If acquiring outside a task, explicitly set a sensible deadline matching your maximum processing time.
  4. 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

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


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)