guzzle/promises · error · LogicException
Cannot reject a fulfilled promise
Error message
Cannot reject a fulfilled promise
What it means
A FulfilledPromise is already settled, so it can never be rejected; reject() is unconditional in this implementation and always throws this LogicException. Settled promises are immutable by design - fulfilling and rejecting are mutually exclusive terminal states.
Solutions
- Only reject promises that are still pending; track settlement with a boolean or restrict reject() calls to the unsettled branch.
- Create a new RejectedPromise (or new Promise + reject()) when you need a rejection outcome.
- Route error handling through ->otherwise()/->then(null, $onRejected) on the fulfilled promise rather than trying to mutate it.
Example fix
// before $fulfilled->reject($reason); // always LogicException // after $rejected = new \GuzzleHttp\Promise\RejectedPromise($reason);
Defensive patterns
Strategy: type-guard
Validate before calling
// reject() on a FulfilledPromise always throws; guard by never calling it
// unless you know the concrete class is not settled:
if ($p instanceof \GuzzleHttp\Promise\Promise && $p->getState() === 'pending') {
$p->reject($reason);
} Type guard
function canReject(\GuzzleHttp\Promise\PromiseInterface $p): bool
{
return $p instanceof \GuzzleHttp\Promise\Promise && $p->getState() === 'pending';
} Try / catch
try {
$promise->reject($reason);
} catch (\LogicException $e) {
// already fulfilled; nothing to do
} Prevention
- Branch settlement logic so reject() only runs on the failure path of a pending promise.
- Track fulfillment with application-level flags when sharing promises across components.
- Use otherwise()/then(null, $fn) to observe failures instead of mutating settled promises.
When it happens
Trigger: Calling ->reject($reason) on any FulfilledPromise instance, no matter the argument. There is no argument value that avoids the throw.
Common situations: Branching code that settles a promise without tracking whether it was already fulfilled; error handlers that reject a promise even after a success path completed; shared promise objects settled from multiple callbacks.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot resolve a fulfilled promise
- Cannot resolve a rejected promise
- Cannot reject a rejected promise
- Not enough promises to fulfill count
- You cannot create a FulfilledPromise with a promise.
AI-assisted analysis of guzzle/promises@42118e66a5 (2026-09-14).
Data as JSON: /api/errors/351d0a40eed0d5c7.
Report an issue: GitHub.
Appendix: source
Thrown at src/FulfilledPromise.php:104
{
return $unwrap ? $this->value : null;
}
public function getState(): string
{
return self::FULFILLED;
}
public function resolve($value = null): void
{
if ($value !== $this->value) {
throw new \LogicException('Cannot resolve a fulfilled promise');
}
}
public function reject($reason): void
{
throw new \LogicException('Cannot reject a fulfilled promise');
}
public function cancel(): void
{
// pass
}
}
View on GitHub (pinned to 42118e66a5)