hibiken/asynq · error
Unique TTL cannot be less than 1s
Error message
Unique TTL cannot be less than 1s
What it means
Raised in composeOptions when a uniqueOption TTL is less than 1 second. Uniqueness locks are checked with second-level granularity, so a sub-second TTL would be meaningless; the client rejects it upfront rather than enqueueing with a broken uniqueness guarantee.
Solutions
- Use a TTL of at least 1*time.Second.
- If you need millisecond-scale dedup, implement your own lock (e.g. Redis SETNX) instead of Unique.
- Fix config unit conversion so the value is seconds-based.
Example fix
// before asynq.Unique(500 * time.Millisecond) // after asynq.Unique(1 * time.Second)
Defensive patterns
Strategy: validation
Validate before calling
const minUniqueTTL = time.Second
if ttl < minUniqueTTL { return fmt.Errorf("unique TTL %v below minimum %v", ttl, minUniqueTTL) } Type guard
func validUniqueTTL(d time.Duration) bool { return d >= time.Second } Try / catch
// Prefer validation; otherwise treat as programmer error:
if strings.Contains(err.Error(), "Unique TTL cannot be less than 1s") { log.Fatal("fix unique TTL config") } Prevention
- Define unique TTLs as named time.Duration constants
- Audit config loading that converts ms/s incorrectly
- Clamp TTL to a 1s floor before passing to asynq.Unique
When it happens
Trigger: Calling client.Enqueue(task, asynq.Unique(500*time.Millisecond)) or Unique(0), or passing a duration computed from config that resolves below 1s (e.g. Unique(ttl) with ttl in ms units).
Common situations: Confusing milliseconds with seconds when loading TTL from config; passing time.Duration(intValue) where intValue was meant as milliseconds; wanting very short dedup windows.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07).
Data as JSON: /api/errors/e5fa5de4e6d583e8.
Report an issue: GitHub.
Appendix: source
Thrown at client.go:305
qname := string(opt)
if err := base.ValidateQueueName(qname); err != nil {
return option{}, err
}
res.queue = qname
case taskIDOption:
id := string(opt)
if isBlank(id) {
return option{}, errors.New("task ID cannot be empty")
}
res.taskID = id
case timeoutOption:
res.timeout = time.Duration(opt)
case deadlineOption:
res.deadline = time.Time(opt)
case uniqueOption:
ttl := time.Duration(opt)
if ttl < 1*time.Second {
return option{}, errors.New("Unique TTL cannot be less than 1s")
}
res.uniqueTTL = ttl
case processAtOption:
res.processAt = time.Time(opt)
case processInOption:
res.processAt = time.Now().Add(time.Duration(opt))
case retentionOption:
res.retention = time.Duration(opt)
case groupOption:
key := string(opt)
if isBlank(key) {
return option{}, errors.New("group key cannot be empty")
}
res.group = key
case headerOption:
key, value := opt[0], opt[1]
res.headers[key] = value
default:View on GitHub (pinned to d135f1439b)