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

  1. Never call resolve() on a promise that may already be rejected; settle only pending promises.
  2. Create a new Promise/FulfilledPromise when you need a fulfillment outcome.
  3. 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

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


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)