guzzle/promises · error · InvalidArgumentException
You cannot create a FulfilledPromise with a promise.
Error message
You cannot create a FulfilledPromise with a promise.
What it means
A FulfilledPromise represents an already-known fulfilled value, so its constructor rejects any object exposing a then() method (a promise or thenable). Wrapping a promise inside a fulfilled promise would create a promise-of-a-promise, which this library's unwrapping semantics cannot represent. Unwrap the promise to its concrete value first.
Solutions
- Resolve the inner promise first: pass $promise->wait() to FulfilledPromise.
- Use \GuzzleHttp\Promise\resolve($promise) which unwraps thenables into a settled promise instead of constructing manually.
- If the intent was conditional settling, create a new Promise() and call resolve()/reject() on it with the inner outcome.
- Type-check inputs before construction and reject promises early in your own API.
Example fix
// before $wrapped = new FulfilledPromise($innerPromise); // InvalidArgumentException // after $wrapped = \GuzzleHttp\Promise\resolve($innerPromise->wait());
Defensive patterns
Strategy: type-guard
Validate before calling
if (is_object($value) && method_exists($value, 'then')) {
throw new \InvalidArgumentException('Value must not be a promise/thenable');
} Type guard
function assertNotThenable($value): void
{
if (is_object($value) && method_exists($value, 'then')) {
throw new \InvalidArgumentException('Cannot wrap a promise; unwrap it first.');
}
} Try / catch
try {
$p = new \GuzzleHttp\Promise\FulfilledPromise($value);
} catch (\InvalidArgumentException $e) {
$p = \GuzzleHttp\Promise\resolve($value); // unwraps thenables
} Prevention
- Never construct FulfilledPromise directly from values of unknown origin; use \GuzzleHttp\Promise\resolve() which handles thenables.
- Keep functions that consume values separate from those that consume promises.
- Check method_exists($v, 'then') before wrapping third-party objects.
When it happens
Trigger: new FulfilledPromise($x) where $x is any object with a then() method, e.g. another FulfilledPromise, RejectedPromise, Promise, PromiseInterface implementation, or foreign thenable.
Common situations: Passing an unresolved promise into code that expects a plain value; converting from another promise library; double-wrapping results of queue()->run() or coroutines; caching layers that wrap values indiscriminately.
Related errors
- You cannot create a RejectedPromise with a promise.
- Not enough promises to fulfill count
- 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/ff9571476c9cb3c7.
Report an issue: GitHub.
Appendix: source
Thrown at src/FulfilledPromise.php:31
* @template TValue = mixed
* @template TReason = never
*
* @implements PromiseInterface<TValue, TReason>
*
* @final
*/
class FulfilledPromise implements PromiseInterface
{
/** @var TValue */
private $value;
/**
* @param TValue $value
*/
public function __construct($value)
{
if (is_object($value) && method_exists($value, 'then')) {
throw new \InvalidArgumentException(
'You cannot create a FulfilledPromise with a promise.'
);
}
$this->value = $value;
}
/**
* @template TFulfilledValue = never
* @template TFulfilledReason = never
* @template TRejectedValue = never
* @template TRejectedReason = never
*
* @param (callable(TValue): (TFulfilledValue|PromiseInterface<TFulfilledValue, TFulfilledReason>))|null $onFulfilled Invoked when the promise fulfills.
* @param (callable(TReason): (TRejectedValue|PromiseInterface<TRejectedValue, TRejectedReason>))|null $onRejected Invoked when the promise is rejected.
*
* @return ($onFulfilled is null ? self<TValue, TReason> : PromiseInterface<TFulfilledValue, TFulfilledReason|\Throwable>)
*/View on GitHub (pinned to 42118e66a5)