guzzle/promises · error · dynamic (Create::exceptionFor)
exceptionFor($this->reason)
Error message
exceptionFor($this->reason)
What it means
RejectedPromise::wait(true) always throws: the promise is already settled as rejected, so wait() converts its stored rejection reason into an exception via Create::exceptionFor($this->reason) and throws it. Unlike Promise::wait(), no resolution can occur - a RejectedPromise is immutable, so every wait(true) call rethrows the same reason.
Solutions
- Wrap the wait() call in try/catch and inspect the caught exception, which is the original rejection reason.
- Handle rejection with ->otherwise() or ->then(null, $onRejected) instead of waiting on the rejected promise.
- If you only need settlement, call wait(false), which returns null without throwing.
- Restructure code so a RejectedPromise is not awaited directly, e.g. recover with ->otherwise(...) to produce a fulfilled promise first.
Example fix
// before
$result = $rejectedPromise->wait(); // always throws the reason
// after
try {
$rejectedPromise->wait();
} catch (\Throwable $e) {
// $e is the reason stored in the RejectedPromise
}
// or, without throwing:
$rejectedPromise->wait(false); Defensive patterns
Strategy: try-catch
Validate before calling
// A RejectedPromise is rejected by construction - check before waiting:
use GuzzleHttp\Promise\RejectedPromise;
if (!$promise instanceof RejectedPromise) {
$value = $promise->wait();
} Type guard
// Reject the eager rejected-promise case before unwrapping
function safeWait(\GuzzleHttp\Promise\PromiseInterface $p) {
if ($p instanceof \GuzzleHttp\Promise\RejectedPromise) {
return null; // wait(true) would throw; do not unwrap
}
return $p->wait();
} Try / catch
try {
$rejectedPromise->wait();
} catch (\Throwable $reason) {
// $reason is exactly the reason the RejectedPromise was constructed with
} Prevention
- Remember RejectedPromise::wait(true) throws unconditionally - never call it expecting a value.
- Use wait(false) for side-effect-free, non-throwing settlement.
- Prefer ->otherwise() recovery over awaiting a known-rejected promise.
- Convert eager rejections with ->otherwise() so downstream code always sees a thenable that resolves normally.
- In tests expecting a reason, catch the exception from wait() and assert on it explicitly.
When it happens
Trigger: Calling ->wait() on any instance created with new RejectedPromise($reason) (or returned by helpers that reject eagerly), with the default $unwrap = true. Throws immediately regardless of prior state, because the rejection reason is fixed at construction.
Common situations: Test fixtures that seed a RejectedPromise and call wait() to assert the reason surfaces; code paths that short-circuit to a rejected promise (e.g. invalid input checked synchronously before async work) and are later awaited; mixing eager RejectedPromise values into code that assumes wait() returns a value.
Related errors
AI-assisted analysis of guzzle/promises@42118e66a5 (2026-09-14).
Data as JSON: /api/errors/f3fc97f2050020c3.
Report an issue: GitHub.
Appendix: source
Thrown at src/RejectedPromise.php:93
}
/**
* @template TRejectedValue = never
* @template TRejectedReason = never
*
* @param callable(TReason): (TRejectedValue|PromiseInterface<TRejectedValue, TRejectedReason>) $onRejected Invoked when the promise is rejected.
*
* @return PromiseInterface<TRejectedValue, TRejectedReason|\Throwable>
*/
public function otherwise(callable $onRejected): PromiseInterface
{
return $this->then(null, $onRejected);
}
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) {View on GitHub (pinned to 42118e66a5)