[pest-plugin-browser] Client::execute() loops forever once the Playwright websocket is closed (run hangs at 100% CPU or exhausts memory)
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 11.7k
- Forks
- 538
- Avg merge
- 4d 11h
- Merged PRs (30d)
- 8
Description
This concerns pestphp/pest-plugin-browser (4.3.1; the same code is on its 5.x branch, 5.0.1). That repository has issues disabled, hence the report here.
What happens
When the browser or the playwright run-server process dies during a run, every later call into Playwright hangs the PHP process instead of failing.
Pest\Browser\Playwright\Client::execute() reads responses in a while (true) loop:
while (true) {
$responseJson = $this->fetch($this->websocketConnection); // (string) $client->receive()?->read()
$response = json_decode($responseJson, true);
...
yield $response;
if ((isset($response['id']) && $response['id'] === $requestId) || ...) {
break;
}
}
Amp\Websocket\Client\WebsocketConnection::receive() returns null once the connection is closed, so fetch() returns '', json_decode('') returns null, nothing matches the request id, and the loop never ends:
- consumers that iterate the generator with
foreach(processResultResponse,querySelectorAll, locator counts, …) spin at 100% CPU forever — apest --coveragerun sat like that for 40 minutes with no output; - consumers that call
iterator_to_array()(processVoidResponse:goto,close, …) accumulatenullentries untilmemory_limit:Allowed memory size of 4294967296 bytes exhausted (tried to allocate 4294967304 bytes) at src/Playwright/Concerns/InteractsWithPlaywright.php:123.
The first symptom in our log was Amp\Websocket\WebsocketClosedException: Client unexpectedly closed; Code 1006 (ABNORMAL_CLOSE); Reason: "Writing to the client failed" thrown by sendText(); the next execute() call reached the read loop and never returned.
Reproduction
- Any browser test file with a handful of tests.
- While it runs, kill the Playwright server:
pkill -9 -f "playwright run-server". - The test running at that moment never finishes (CPU at 100%), or the process dies with the memory error above.
Seen with pestphp/pest-plugin-browser 4.3.1 (Pest 4.7.5, PHP 8.4, Chromium via playwright run-server --mode launchServer); the loop is unchanged on 5.x (5.0.1).
Suggested fix
Treat a closed connection as an error instead of an empty response, e.g. in execute():
$message = $this->websocketConnection->receive();
if ($message === null) {
throw new BrowserAlreadyClosedException(); // or a dedicated exception naming the closed connection
}
$responseJson = $message->read();
With that, the test that lost the browser fails at once with a clear reason, and the following tests fail the same way instead of hanging the run.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in Pest\Browser\Playwright\Client::execute(), especially the websocket receive loop described in the issue, and reproduce the failure by killing the Playwright server during a browser test. Treat a closed receive as an error so the run fails promptly with a clear browser-closed reason instead of spinning or accumulating responses.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100