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
- Return the promise from your function instead of resolving it with itself (thenables are unwrapped automatically).
- Resolve with the actual awaited value or a different promise object, never $this.
- 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
- In pass-through functions, return the promise instead of resolving it with itself.
- Never reference $this inside a promise's own wait/resolver function as the settling value.
- Add an assertion $value !== $this in custom PromiseInterface implementations.
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
- Not enough promises to fulfill count
- You cannot create a FulfilledPromise with a promise.
- Cannot resolve a fulfilled promise
- Cannot reject a fulfilled promise
- should never be serialized
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)