File: src/Transport/JsonRpc/JsonRpcTransport.php
Description
I was successfully able to launch chromium & webkit. But Firefox always failed to: playwright firefox JSON-RPC request 1 timed out
PlaywrightConfig accepts a timeoutMs parameter that is documented as controlling the timeout for browser operations. It is correctly used to set the Symfony Process timeout (how long the Node subprocess may run). However, it is not passed to ProcessJsonRpcClient, which has its own separate defaultTimeoutMs that is hardcoded to 30000.0 ms.
This means any individual RPC request — including browser.launch() — will time out after 30 seconds regardless of what timeoutMs is set to. Increasing timeoutMs in PlaywrightConfig has no effect on actual request timeouts.
Steps to reproduce
// Firefox launch can take >30 s in server environments.
// Setting timeoutMs: 90000 should give 90 s for each operation.
$config = new PlaywrightConfig(timeoutMs: 90000);
$client = PlaywrightFactory::create($config);
$browser = $client->firefox()->withHeadless(true)->launch(); // always throws after 30 s
Playwright\Exception\NetworkException: JSON-RPC request 1 timed out after 30000ms
Root cause
In JsonRpcTransport::connect(), ProcessJsonRpcClient is constructed with a hardcoded timeout:
// src/Transport/JsonRpc/JsonRpcTransport.php
$this->process = $this->processLauncher->start(
$command,
$cwd,
$env,
is_int($timeout) ? (float) $timeout : $timeout // ← process timeout uses config correctly
);
$this->client = new ProcessJsonRpcClient(
process: $this->process,
processLauncher: $this->processLauncher,
logger: $this->logger,
defaultTimeoutMs: 30000.0, // ← hardcoded, ignores $config->timeoutMs
);
The $timeout variable (derived from $this->config['timeout'], which comes from PlaywrightConfig::timeoutMs / 1000) is available in scope at this point but is not forwarded.
Fix
$this->client = new ProcessJsonRpcClient(
process: $this->process,
processLauncher: $this->processLauncher,
logger: $this->logger,
defaultTimeoutMs: null !== $timeout ? (float) ($timeout * 1000) : 30000.0,
);
This uses the configured timeout when one is provided and falls back to the existing 30 s default otherwise — fully backwards compatible.
Why this matters
Slow browser engines (particularly Firefox on first launch in restricted server environments) can take significantly longer than 30 s to initialise. There is currently no way to work around this through the public API — PlaywrightConfig(timeoutMs: 90000) appears to be the correct knob to turn, but it has no effect on the operation that actually times out.
File: src/Transport/JsonRpc/JsonRpcTransport.php
Description
I was successfully able to launch chromium & webkit. But Firefox always failed to: playwright firefox JSON-RPC request 1 timed out
PlaywrightConfig accepts a timeoutMs parameter that is documented as controlling the timeout for browser operations. It is correctly used to set the Symfony Process timeout (how long the Node subprocess may run). However, it is not passed to ProcessJsonRpcClient, which has its own separate defaultTimeoutMs that is hardcoded to 30000.0 ms.
This means any individual RPC request — including browser.launch() — will time out after 30 seconds regardless of what timeoutMs is set to. Increasing timeoutMs in PlaywrightConfig has no effect on actual request timeouts.
Steps to reproduce
Playwright\Exception\NetworkException: JSON-RPC request 1 timed out after 30000ms
Root cause
The $timeout variable (derived from $this->config['timeout'], which comes from PlaywrightConfig::timeoutMs / 1000) is available in scope at this point but is not forwarded.
Fix
This uses the configured timeout when one is provided and falls back to the existing 30 s default otherwise — fully backwards compatible.
Why this matters
Slow browser engines (particularly Firefox on first launch in restricted server environments) can take significantly longer than 30 s to initialise. There is currently no way to work around this through the public API — PlaywrightConfig(timeoutMs: 90000) appears to be the correct knob to turn, but it has no effect on the operation that actually times out.