[Bug] All protocol calls can block indefinitely: timeouts are enforced on the driver side, so a frozen driver/browser process never surfaces TimeoutError
Summary
In a production screenshot microservice (Playwright Java embedded in a Spring Boot app, 10 worker threads, each with its own thread-confined Playwright/Browser/Page chain), all 10 worker threads blocked indefinitely inside waitForFunction for 16+ hours, despite each call setting setTimeout(6000). A thread dump showed every thread parked in PipeTransport.poll → ArrayBlockingQueue.poll, waiting for messages from the driver that never arrive. There were no errors, no page events, and no pipe EOF — just silence.
"chartProcessor-1" ... java.lang.Thread.State: TIMED_WAITING (parking)
at java.util.concurrent.locks.LockSupport.parkNanos(...)
at java.util.concurrent.ArrayBlockingQueue.poll(ArrayBlockingQueue.java:435)
at com.microsoft.playwright.impl.PipeTransport.poll(PipeTransport.java:68)
at com.microsoft.playwright.impl.Connection.processOneMessage(Connection.java:205)
at com.microsoft.playwright.impl.ChannelOwner.runUntil(ChannelOwner.java:130)
at com.microsoft.playwright.impl.Connection.sendMessage(Connection.java:130)
at com.microsoft.playwright.impl.ChannelOwner.sendMessage(ChannelOwner.java:118)
at com.microsoft.playwright.impl.FrameImpl.waitForFunctionImpl(FrameImpl.java:877)
...
Root cause (from decompiled bytecode, 1.52.0)
FrameImpl.waitForFunctionImpl is a single synchronous sendMessage("waitForFunction", json); the timeout option is only serialized into the protocol message. There is no client-side deadline.
ChannelOwner.runUntil loops while (!waitable.isDone()) connection.processOneMessage() — no deadline check in the loop.
Connection.processOneMessage calls transport.poll(Duration.ofMillis(10)) and simply waits for the next driver message.
- The configured timeout is enforced on the Node driver side (e.g.
progress.js in the bundled driver). When the driver process itself — or the browser process it talks to — hangs (in our case, consistent with container-level resource pressure freezing all driver + browser child processes at the same moment), no message is ever produced, the driver-side timers never fire, and every pending protocol call blocks forever.
Note that all threads had fully independent Playwright/Browser/Page chains (per-thread confinement, no sharing), and all of them froze simultaneously — which is how we concluded this was an environment-level event rather than an application bug. Even so, the library turning that into a permanent, silent hang of the host application is a robustness problem on the Java client side.
Impact
Any driver/browser freeze converts into a permanent hang of calling threads; all configured timeouts become meaningless in this state. For services embedding Playwright in production (rather than test usage), this is severe: worker pools fill up, queues backpressure, and the service stops processing entirely.
Related issues
Suggested improvement
Enforce a client-side deadline in ChannelOwner.runUntil / Connection.processOneMessage: track the per-call deadline, and when a poll returns nothing past the deadline, throw TimeoutError locally (optionally with a distinct message such as "driver did not respond"). This would make the Java client consistent with other language clients, where timeout enforcement is in-process, and would make driver/browser hangs observable and recoverable instead of silently fatal.
Environment
[Bug] All protocol calls can block indefinitely: timeouts are enforced on the driver side, so a frozen driver/browser process never surfaces TimeoutError
Summary
In a production screenshot microservice (Playwright Java embedded in a Spring Boot app, 10 worker threads, each with its own thread-confined
Playwright/Browser/Pagechain), all 10 worker threads blocked indefinitely insidewaitForFunctionfor 16+ hours, despite each call settingsetTimeout(6000). A thread dump showed every thread parked inPipeTransport.poll→ArrayBlockingQueue.poll, waiting for messages from the driver that never arrive. There were no errors, no page events, and no pipe EOF — just silence.Root cause (from decompiled bytecode, 1.52.0)
FrameImpl.waitForFunctionImplis a single synchronoussendMessage("waitForFunction", json); thetimeoutoption is only serialized into the protocol message. There is no client-side deadline.ChannelOwner.runUntilloopswhile (!waitable.isDone()) connection.processOneMessage()— no deadline check in the loop.Connection.processOneMessagecallstransport.poll(Duration.ofMillis(10))and simply waits for the next driver message.progress.jsin the bundled driver). When the driver process itself — or the browser process it talks to — hangs (in our case, consistent with container-level resource pressure freezing all driver + browser child processes at the same moment), no message is ever produced, the driver-side timers never fire, and every pending protocol call blocks forever.Note that all threads had fully independent
Playwright/Browser/Pagechains (per-thread confinement, no sharing), and all of them froze simultaneously — which is how we concluded this was an environment-level event rather than an application bug. Even so, the library turning that into a permanent, silent hang of the host application is a robustness problem on the Java client side.Impact
Any driver/browser freeze converts into a permanent hang of calling threads; all configured timeouts become meaningless in this state. For services embedding Playwright in production (rather than test usage), this is severe: worker pools fill up, queues backpressure, and the service stops processing entirely.
Related issues
TimeoutErrorbeing raised by driver-sideprogress.js, i.e. the same design; the report also states it is reproducible in 1.58.0.Suggested improvement
Enforce a client-side deadline in
ChannelOwner.runUntil/Connection.processOneMessage: track the per-call deadline, and when a poll returns nothing past the deadline, throwTimeoutErrorlocally (optionally with a distinct message such as "driver did not respond"). This would make the Java client consistent with other language clients, where timeout enforcement is in-process, and would make driver/browser hangs observable and recoverable instead of silently fatal.Environment