laravel/framework · error · LogicException
A debounced job cannot also implement ShouldBeUnique.
Error message
A debounced job cannot also implement ShouldBeUnique.
What it means
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).
Source
Thrown at src/Illuminate/Foundation/Bus/PendingDispatch.php:252
/**
* Acquire a debounce lock for the job and set its delay.
*
* @return void
*
* @throws LogicException
*/
protected function acquireDebounceLock()
{
$debounceFor = $this->getAttributeValue($this->job, DebounceFor::class, 'debounceFor');
if ($debounceFor === null) {
return;
}
$lock = new DebounceLock(Container::getInstance()->make(Cache::class));
if ($this->job instanceof ShouldBeUnique) {
throw new LogicException('A debounced job cannot also implement ShouldBeUnique.');
}
$result = $lock->acquire(
$this->job, $debounceFor
);
$this->job->debounceOwner = $result['owner'];
if (is_null($this->job->delay)) {
$this->job->delay = $result['maxWaitExceeded'] ? 0 : $debounceFor;
}
}
/**
* Get the underlying job instance.
*
* @return mixed
*/View on GitHub (pinned to bd6b5437e6)
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.
Example fix
// before
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Queue\Attributes\DebounceFor;
class SendDigest implements ShouldBeUnique
{
#[DebounceFor(60)]
public int $debounceFor = 60;
}
// after — pick one mechanism
class SendDigest
{
#[DebounceFor(60)]
public int $debounceFor = 60;
} Defensive patterns
Strategy: type-guard
Validate before calling
use Illuminate\Contracts\Queue\ShouldBeUnique;
$job = new SomeJob();
if ($job instanceof ShouldBeUnique && (new \ReflectionClass($job))->hasProperty('debounceFor')) {
throw new \LogicException('Cannot dispatch: job is both ShouldBeUnique and debounced.');
} Type guard
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Queue\Attributes\DebounceFor;
function isDebounced(object|string $job): bool
{
$r = new \ReflectionClass(is_object($job) ? $job : $job);
if ($r->hasProperty('debounceFor')) {
return true;
}
return ! empty($r->getAttributes(DebounceFor::class));
}
// Guard dispatch:
if (isDebounced(SomeJob::class) && is_subclass_of(SomeJob::class, ShouldBeUnique::class)) {
throw new \LogicException('Pick one: debounce OR ShouldBeUnique, not both.');
} Try / catch
// LogicException indicates a programmer error; fix the class definition, do not catch at dispatch.
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Ably error: %s
- Queue resolver did not return a Queue implementation.
- Attempted to batch job [%s], but it does not use the Batchab
- To enable support for closure jobs, please install the illum
- To enable support for closure jobs, please install the illum
AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06).
Data as JSON: /data/errors/7dbc2a23839e3d4a.json.
Report an issue: GitHub.