{"id":"7dbc2a23839e3d4a","repo":"laravel/framework","slug":"a-debounced-job-cannot-also-implement-shouldbeuniq","errorCode":null,"errorMessage":"A debounced job cannot also implement ShouldBeUnique.","messagePattern":"A debounced job cannot also implement ShouldBeUnique\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/Illuminate/Foundation/Bus/PendingDispatch.php","lineNumber":252,"sourceCode":"    /**\n     * Acquire a debounce lock for the job and set its delay.\n     *\n     * @return void\n     *\n     * @throws LogicException\n     */\n    protected function acquireDebounceLock()\n    {\n        $debounceFor = $this->getAttributeValue($this->job, DebounceFor::class, 'debounceFor');\n\n        if ($debounceFor === null) {\n            return;\n        }\n\n        $lock = new DebounceLock(Container::getInstance()->make(Cache::class));\n\n        if ($this->job instanceof ShouldBeUnique) {\n            throw new LogicException('A debounced job cannot also implement ShouldBeUnique.');\n        }\n\n        $result = $lock->acquire(\n            $this->job, $debounceFor\n        );\n\n        $this->job->debounceOwner = $result['owner'];\n\n        if (is_null($this->job->delay)) {\n            $this->job->delay = $result['maxWaitExceeded'] ? 0 : $debounceFor;\n        }\n    }\n\n    /**\n     * Get the underlying job instance.\n     *\n     * @return mixed\n     */","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/laravel/framework/blob/bd6b5437e6ad87bb49f9b426724f07a9f64e9683/src/Illuminate/Foundation/Bus/PendingDispatch.php#L234-L270","documentation":"Thrown by PendingDispatch::acquireDebounceLock() when a job both declares a debounce window (via the DebounceFor attribute or a debounceFor property) and implements ShouldBeUnique. Debounce coalesces repeated dispatches of the same job into one using a lock keyed by the job identity; ShouldBeUnique prevents duplicate dispatches entirely. The two mechanisms conflict and could double-protect or deadlock, so Laravel rejects the combination at dispatch time as a LogicException (a programmer error, not a runtime fault).","triggerScenarios":"Defining a job class that uses #[DebounceFor(60)] (or public property $debounceFor) and also `implements ShouldBeUnique` (or ShouldBeUniqueUntilProcessing). Triggered at dispatch: SomeJob::dispatch($payload); — the exception fires before the job reaches the queue.","commonSituations":"Adding ShouldBeUnique to an existing debounced job for 'extra safety'. Copying a unique-job pattern onto a job that was later given a debounce attribute. Upgrading Laravel to a version that introduced DebounceFor and combining both features without reading the constraint.","solutions":["Remove `implements ShouldBeUnique` (and ShouldBeUniqueUntilProcessing) from the debounced job class — debounce already prevents duplicates.","If you truly need uniqueness semantics, remove the DebounceFor attribute/property and rely on ShouldBeUnique instead.","Use ShouldBeUnique on a different job than the debounced one and split responsibilities."],"exampleFix":"// before\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Queue\\Attributes\\DebounceFor;\n\nclass SendDigest implements ShouldBeUnique\n{\n    #[DebounceFor(60)]\n    public int $debounceFor = 60;\n}\n\n// after — pick one mechanism\nclass SendDigest\n{\n    #[DebounceFor(60)]\n    public int $debounceFor = 60;\n}","handlingStrategy":"type-guard","validationCode":"use Illuminate\\Contracts\\Queue\\ShouldBeUnique;\n\n$job = new SomeJob();\nif ($job instanceof ShouldBeUnique && (new \\ReflectionClass($job))->hasProperty('debounceFor')) {\n    throw new \\LogicException('Cannot dispatch: job is both ShouldBeUnique and debounced.');\n}","typeGuard":"use Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Queue\\Attributes\\DebounceFor;\n\nfunction isDebounced(object|string $job): bool\n{\n    $r = new \\ReflectionClass(is_object($job) ? $job : $job);\n    if ($r->hasProperty('debounceFor')) {\n        return true;\n    }\n    return ! empty($r->getAttributes(DebounceFor::class));\n}\n\n// Guard dispatch:\nif (isDebounced(SomeJob::class) && is_subclass_of(SomeJob::class, ShouldBeUnique::class)) {\n    throw new \\LogicException('Pick one: debounce OR ShouldBeUnique, not both.');\n}","tryCatchPattern":"// LogicException indicates a programmer error; fix the class definition, do not catch at dispatch.","preventionTips":["Do not combine ShouldBeUnique with DebounceFor on the same job — debounce already coalesces duplicates.","Add a static analysis/CI rule scanning for jobs implementing ShouldBeUnique that also use DebounceFor.","Document debounce vs unique semantics in the team's job-authoring guide."],"tags":["queue","jobs","debounce","should-be-unique","logic-exception"],"analyzedSha":"bd6b5437e6ad87bb49f9b426724f07a9f64e9683","analyzedAt":"2026-08-06T00:28:32.783Z","schemaVersion":2}