guzzle/promises · error · LogicException

Cannot fulfill or reject a promise with itself

Error message

Cannot fulfill or reject a promise with itself

What it means

Promise::settle() throws this LogicException when a promise is fulfilled or rejected with itself as the settling value ($value === $this). A self-referencing resolution would create an unresolvable cycle, so the library rejects it eagerly with a clear invariant violation instead of hanging or recursing infinitely.

Solutions

  1. Return the promise from your function instead of resolving it with itself (thenables are unwrapped automatically).
  2. Resolve with the actual awaited value or a different promise object, never $this.
  3. In custom PromiseInterface implementations, assert $value !== $this before delegating to settle().

Example fix

// before
function passThrough($p) { $p->resolve($p); return $p; } // LogicException
// after
function passThrough($p) { return $p; } // promise returned; callers can then() it
Defensive patterns

Strategy: validation

Validate before calling

if ($value === $promise) {
    throw new \InvalidArgumentException('Cannot settle a promise with itself');
}
$promise->resolve($value);

Type guard

function isSelfReference(\GuzzleHttp\Promise\Promise $p, $value): bool
{
    return $value === $p;
}

Try / catch

try {
    $promise->resolve($value);
} catch (\LogicException $e) {
    // self-settlement cycle; return/chain the promise instead
}

Prevention

When it happens

Trigger: Calling $promise->resolve($promise) or $promise->reject($promise) on the very same Promise instance; passing the promise back as the result from inside its own wait function or a resolver callback.

Common situations: Recursive/async code that returns the promise itself instead of its eventual value; forwarding functions like function fwd($p){ return $p->resolve($p); } meant to pass through a thenable; copy-paste in wrapper classes that confuse the wrapper with the inner promise.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/Promise.php:178

    public function reject($reason): void
    {
        $this->settle(self::REJECTED, $reason);
    }

    private function settle(string $state, $value): void
    {
        if ($this->state !== self::PENDING) {
            // Ignore calls with the same resolution.
            if ($state === $this->state && $value === $this->result) {
                return;
            }
            throw $this->state === $state
                ? new \LogicException("The promise is already {$state}.")
                : new \LogicException("Cannot change a {$this->state} promise to {$state}");
        }

        if ($value === $this) {
            throw new \LogicException('Cannot fulfill or reject a promise with itself');
        }

        // Clear out the state of the promise but stash the handlers.
        $this->state = $state;
        $this->result = $value;
        $handlers = $this->handlers;
        $this->handlers = null;
        $this->waitList = $this->waitFn = null;
        $this->cancelFn = null;

        if (!$handlers) {
            return;
        }

        // If the value was not a settled promise or a thenable, then resolve
        // it in the task queue using the correct ID.
        if (!is_object($value) || !method_exists($value, 'then')) {
            $id = $state === self::FULFILLED ? 1 : 2;

View on GitHub (pinned to 42118e66a5)