{"record":{"id":"e5fa5de4e6d583e8","repo":"hibiken/asynq","slug":"unique-ttl-cannot-be-less-than-1s","errorCode":null,"errorMessage":"Unique TTL cannot be less than 1s","messagePattern":"Unique TTL cannot be less than 1s","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client.go","lineNumber":305,"sourceCode":"\t\t\tqname := string(opt)\n\t\t\tif err := base.ValidateQueueName(qname); err != nil {\n\t\t\t\treturn option{}, err\n\t\t\t}\n\t\t\tres.queue = qname\n\t\tcase taskIDOption:\n\t\t\tid := string(opt)\n\t\t\tif isBlank(id) {\n\t\t\t\treturn option{}, errors.New(\"task ID cannot be empty\")\n\t\t\t}\n\t\t\tres.taskID = id\n\t\tcase timeoutOption:\n\t\t\tres.timeout = time.Duration(opt)\n\t\tcase deadlineOption:\n\t\t\tres.deadline = time.Time(opt)\n\t\tcase uniqueOption:\n\t\t\tttl := time.Duration(opt)\n\t\t\tif ttl < 1*time.Second {\n\t\t\t\treturn option{}, errors.New(\"Unique TTL cannot be less than 1s\")\n\t\t\t}\n\t\t\tres.uniqueTTL = ttl\n\t\tcase processAtOption:\n\t\t\tres.processAt = time.Time(opt)\n\t\tcase processInOption:\n\t\t\tres.processAt = time.Now().Add(time.Duration(opt))\n\t\tcase retentionOption:\n\t\t\tres.retention = time.Duration(opt)\n\t\tcase groupOption:\n\t\t\tkey := string(opt)\n\t\t\tif isBlank(key) {\n\t\t\t\treturn option{}, errors.New(\"group key cannot be empty\")\n\t\t\t}\n\t\t\tres.group = key\n\t\tcase headerOption:\n\t\t\tkey, value := opt[0], opt[1]\n\t\t\tres.headers[key] = value\n\t\tdefault:","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/hibiken/asynq/blob/d135f1439bee74e989b7f9b41ecd542cc87f024a/client.go#L287-L323","documentation":"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.","triggerScenarios":"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).","commonSituations":"Confusing milliseconds with seconds when loading TTL from config; passing time.Duration(intValue) where intValue was meant as milliseconds; wanting very short dedup windows.","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."],"exampleFix":"// before\nasynq.Unique(500 * time.Millisecond)\n// after\nasynq.Unique(1 * time.Second)","handlingStrategy":"validation","validationCode":"const minUniqueTTL = time.Second\nif ttl < minUniqueTTL { return fmt.Errorf(\"unique TTL %v below minimum %v\", ttl, minUniqueTTL) }","typeGuard":"func validUniqueTTL(d time.Duration) bool { return d >= time.Second }","tryCatchPattern":"// Prefer validation; otherwise treat as programmer error:\nif strings.Contains(err.Error(), \"Unique TTL cannot be less than 1s\") { log.Fatal(\"fix unique TTL config\") }","preventionTips":["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"],"tags":["validation","uniqueness","ttl","duration"],"backgroundTag":"invalid-argument-value","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"}