{"record":{"id":"351d0a40eed0d5c7","repo":"guzzle/promises","slug":"cannot-reject-a-fulfilled-promise","errorCode":null,"errorMessage":"Cannot reject a fulfilled promise","messagePattern":"Cannot reject a fulfilled promise","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/FulfilledPromise.php","lineNumber":104,"sourceCode":"    {\n        return $unwrap ? $this->value : null;\n    }\n\n    public function getState(): string\n    {\n        return self::FULFILLED;\n    }\n\n    public function resolve($value = null): void\n    {\n        if ($value !== $this->value) {\n            throw new \\LogicException('Cannot resolve a fulfilled promise');\n        }\n    }\n\n    public function reject($reason): void\n    {\n        throw new \\LogicException('Cannot reject a fulfilled promise');\n    }\n\n    public function cancel(): void\n    {\n        // pass\n    }\n}\n","sourceCodeStart":86,"sourceCodeEnd":112,"githubUrl":"https://github.com/guzzle/promises/blob/42118e66a53c492effaf92bc357e931985d5c6f9/src/FulfilledPromise.php#L86-L112","documentation":"A FulfilledPromise is already settled, so it can never be rejected; reject() is unconditional in this implementation and always throws this LogicException. Settled promises are immutable by design - fulfilling and rejecting are mutually exclusive terminal states.","triggerScenarios":"Calling ->reject($reason) on any FulfilledPromise instance, no matter the argument. There is no argument value that avoids the throw.","commonSituations":"Branching code that settles a promise without tracking whether it was already fulfilled; error handlers that reject a promise even after a success path completed; shared promise objects settled from multiple callbacks.","solutions":["Only reject promises that are still pending; track settlement with a boolean or restrict reject() calls to the unsettled branch.","Create a new RejectedPromise (or new Promise + reject()) when you need a rejection outcome.","Route error handling through ->otherwise()/->then(null, $onRejected) on the fulfilled promise rather than trying to mutate it."],"exampleFix":"// before\n$fulfilled->reject($reason); // always LogicException\n// after\n$rejected = new \\GuzzleHttp\\Promise\\RejectedPromise($reason);","handlingStrategy":"type-guard","validationCode":"// reject() on a FulfilledPromise always throws; guard by never calling it\n// unless you know the concrete class is not settled:\nif ($p instanceof \\GuzzleHttp\\Promise\\Promise && $p->getState() === 'pending') {\n    $p->reject($reason);\n}","typeGuard":"function canReject(\\GuzzleHttp\\Promise\\PromiseInterface $p): bool\n{\n    return $p instanceof \\GuzzleHttp\\Promise\\Promise && $p->getState() === 'pending';\n}","tryCatchPattern":"try {\n    $promise->reject($reason);\n} catch (\\LogicException $e) {\n    // already fulfilled; nothing to do\n}","preventionTips":["Branch settlement logic so reject() only runs on the failure path of a pending promise.","Track fulfillment with application-level flags when sharing promises across components.","Use otherwise()/then(null, $fn) to observe failures instead of mutating settled promises."],"tags":["promises","state","immutability","guzzle"],"backgroundTag":"invalid-state-transition","analyzedSha":"42118e66a53c492effaf92bc357e931985d5c6f9","analyzedAt":"2026-09-14T00:10:14.865Z","contentChangedAt":"2026-09-14T00:10:14.865Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}