guzzle/promises · error · LogicException
Cannot resolve a rejected promise
Error message
Cannot resolve a rejected promise
What it means
RejectedPromise::resolve() always throws this LogicException: a rejected promise is settled and immutable, and the strict identity rule that lets a fulfilled promise be 'resolved' with the same value does not apply here at all. The library fails fast rather than silently ignoring a resolve() on a rejected state.
Solutions
- Never call resolve() on a promise that may already be rejected; settle only pending promises.
- Create a new Promise/FulfilledPromise when you need a fulfillment outcome.
- Track settlement state in your own wrapper (or rely on then()/otherwise() to observe the outcome immutably).
Example fix
// before $rejected->resolve($value); // always LogicException // after $fulfilled = new \GuzzleHttp\Promise\FulfilledPromise($value);
Defensive patterns
Strategy: type-guard
Validate before calling
if ($p instanceof \GuzzleHttp\Promise\RejectedPromise) {
// already rejected; resolve() would throw
return;
}
$p->resolve($value); Type guard
function canResolve(\GuzzleHttp\Promise\PromiseInterface $p): bool
{
return !$p instanceof \GuzzleHttp\Promise\RejectedPromise
&& $p instanceof \GuzzleHttp\Promise\Promise
&& $p->getState() === 'pending';
} Try / catch
try {
$promise->resolve($value);
} catch (\LogicException $e) {
// already rejected; the rejection stands
} Prevention
- Settle each promise from exactly one code path so resolve/reject never race.
- Check getState() === 'pending' before resolving shared promises.
- Treat settled promises as read-only: use then()/otherwise() to derive new outcomes.
When it happens
Trigger: Calling ->resolve($value) (including with the default null) on any RejectedPromise instance. No argument avoids the throw.
Common situations: Generic settle helpers that call resolve() unconditionally without knowing the concrete promise class; race conditions where a rejection won before a resolve path ran; reusing one promise object for multiple operations; porting code from libraries whose resolve() is a no-op on settled promises.
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 reject a fulfilled 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/daf43a8c86dd3457.
Report an issue: GitHub.
Appendix: source
Thrown at src/RejectedPromise.php:106
}
public function wait(bool $unwrap = true)
{
if ($unwrap) {
throw Create::exceptionFor($this->reason);
}
return null;
}
public function getState(): string
{
return self::REJECTED;
}
public function resolve($value = null): void
{
throw new \LogicException('Cannot resolve a rejected promise');
}
public function reject($reason): void
{
if ($reason !== $this->reason) {
throw new \LogicException('Cannot reject a rejected promise');
}
}
public function cancel(): void
{
// pass
}
}
View on GitHub (pinned to 42118e66a5)