guzzle/promises · error · dynamic (Create::exceptionFor)

exceptionFor($this->result)

Error message

exceptionFor($this->result)

What it means

Promise::wait(true) synchronously resolves the promise, then returns the fulfillment value. If the promise settled as rejected, the rejection reason is passed through Create::exceptionFor() and thrown. This is how Guzzle Promises surfaces asynchronous failures to synchronous call sites: the rejection reason becomes a normal PHP exception at the wait() call.

Solutions

  1. Wrap the $promise->wait() call in try/catch and handle the rejection reason exception.
  2. Attach an ->otherwise() or ->then(null, $onRejected) handler before wait() so rejection is handled in the promise chain instead of thrown.
  3. Check $promise->getState() === Promise::FULFILLED and call wait(false) or inspect the state to avoid the unwrap throw when you only need settlement.
  4. Fix the root cause of the rejection (network error, invalid request, upstream failure) rather than only catching it.

Example fix

// before
$value = $promise->wait(); // throws if promise was rejected

// after
try {
    $value = $promise->wait();
} catch (\Throwable $e) {
    // handle the rejection reason surfaced by wait(true)
    $value = null;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before waiting, only unwrap when the promise is settled and fulfilled:
use GuzzleHttp\Promise\Promise;
if ($promise->getState() === Promise::FULFILLED) {
    $value = $promise->wait(); // safe: will not throw
}

Type guard

// Narrow by settlement state before unwrapping
function fulfilledValue(\GuzzleHttp\Promise\PromiseInterface $p) {
    if ($p->getState() === \GuzzleHttp\Promise\Promise::FULFILLED) {
        return $p->wait(); // returns value, never throws
    }
    return null; // pending or rejected - do not unwrap
}

Try / catch

try {
    $value = $promise->wait();
} catch (\GuzzleHttp\Exception\ConnectException $e) {
    // network/connection failure
} catch (\GuzzleHttp\Exception\RequestException $e) {
    // HTTP-level failure
} catch (\Throwable $e) {
    // any other rejection reason thrown by wait(true)
}

Prevention

When it happens

Trigger: Calling $promise->wait() (or wait(true), the default) on a Promise that has been or becomes rejected - e.g. an inner promise rejected with an exception, a coroutine thrown, or rejection() invoked before wait. The thrown value is whatever the promise was rejected with, normalized via Create::exceptionFor().

Common situations: Blocking on an HTTP request with Guzzle that failed (4xx/5xx mapped to exceptions like ConnectException or BadResponseException); a promise chain where an earlier then() handler threw; calling wait() on a promise rejected because a nested coroutine failed; tests that reject promises and expect wait() to propagate the reason.

Related errors


AI-assisted analysis of guzzle/promises@42118e66a5 (2026-09-14). Data as JSON: /api/errors/bf8bfa04a2a88201. Report an issue: GitHub.

Appendix: source

Thrown at src/Promise.php:119

     */
    public function otherwise(callable $onRejected): PromiseInterface
    {
        return $this->then(null, $onRejected);
    }

    public function wait(bool $unwrap = true)
    {
        $this->waitIfPending();

        if ($this->result instanceof PromiseInterface) {
            return $this->result->wait($unwrap);
        }
        if ($unwrap) {
            if ($this->state === self::FULFILLED) {
                return $this->result;
            }
            // It's rejected so "unwrap" and throw an exception.
            throw Create::exceptionFor($this->result);
        }

        return null;
    }

    public function getState(): string
    {
        return $this->state;
    }

    public function cancel(): void
    {
        if ($this->state !== self::PENDING) {
            return;
        }

        $this->waitFn = $this->waitList = null;

View on GitHub (pinned to 42118e66a5)