{"record":{"id":"9631aa1212d70169","repo":"karatelabs/karate","slug":"driver-failed-liveness-probe-on-release-discarding-instead","errorCode":null,"errorMessage":"Driver failed liveness probe on release, discarding instead of returning to pool","messagePattern":"Driver failed liveness probe on release, discarding instead of returning to pool","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"karate-core/src/main/java/io/karatelabs/driver/PooledDriverProvider.java","lineNumber":256,"sourceCode":"            logger.debug(\"Not returning terminated driver to pool\");\n            createdCount.decrementAndGet();\n            return;\n        }\n\n        // Eagerly discard a driver whose channel went sick mid-scenario instead of\n        // returning it to the pool. The acquire-time resetDriver() probe would catch\n        // it eventually, but only AFTER the next scenario inherits the poisoned driver\n        // and eats its own timeout — which is how a single renderer stall under a slow\n        // CI container (\"CDP timeout for: Page.captureScreenshot\") cascaded into a batch\n        // of failures on the same pooled driver. Probing here (cheap, bounded — see\n        // isResponsive()) caps that cross-scenario contagion. We probe BEFORE\n        // cleanScenarioState() so a dead channel can't hang the stopIntercept/onDialog\n        // teardown calls on the full CDP timeout. Note: an eval-based probe can still\n        // pass on a renderer stalled only on compositor/paint work (the screenshot path),\n        // so this is insurance, not a guarantee — the bounded failureScreenshot timeout\n        // covers that case.\n        if (!driver.isResponsive()) {\n            logger.warn(\"Driver failed liveness probe on release, discarding instead of returning to pool\");\n            closeDriverQuietly(driver);\n            createdCount.decrementAndGet();\n            return;\n        }\n\n        // Tear down scenario-scoped state BEFORE returning to the pool. This is the\n        // \"top-level scenario exit\" hook per DRIVER.md — the owner scenario is done,\n        // so any driver state it installed (intercept handler, dialog handler, etc.)\n        // belongs to a scope that no longer exists and must not leak into the next\n        // scenario that acquires this driver.\n        //\n        // Why this matters (context for future maintainers): intercept.feature's\n        // wildcard-pattern scenario set Fetch.enable via driver.intercept() and never\n        // called stopIntercept. The driver went back to the pool with Fetch.enable\n        // still active and a stale JS InterceptHandler from a destroyed scenario.\n        // Every subsequent Page.navigate on that driver paused ALL subresource\n        // requests through our onRequestPaused event handler, which runs synchronously\n        // on the CDP websocket dispatch thread. A heavy page (HTML + CSS + JS + etc)","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-core/src/main/java/io/karatelabs/driver/PooledDriverProvider.java#L238-L274","documentation":"When a driver is released back to the pool, the provider probes its liveness with driver.isResponsive(). A driver that fails the probe is closed instead of pooled, so the next borrower does not get a dead channel. This is defensive insurance against CDP channels that died mid-scenario.","triggerScenarios":"Releasing a driver whose websocket/CDP channel died (browser crash, renderer hang) so isResponsive() returns false during release().","commonSituations":"Chrome renderer crash during a long scenario; browser killed by OOM killer; driver object released after a CDP timeout already hinted the channel was sick.","solutions":["None needed for correct operation — the pool self-heals by closing and decrementing createdCount","If frequent, investigate why browsers die (memory limits, headless crashes, stale chrome processes)","Enable debug logging to correlate probe failures with scenario activity"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// discard is automatic; validate driver health yourself before asserting\ndriver.waitUntilReady();\nif (!driver.isResponsive()) { /* request a fresh driver */ }","typeGuard":"boolean driverUsable = d -> !d.isTerminated() && d.isResponsive();","tryCatchPattern":null,"preventionTips":["Keep browser memory in bounds to avoid renderer crashes","Restart the browser process between large suites","Treat 'failed liveness probe' warnings as a signal of browser instability, not a test failure"],"tags":["driver-pool","liveness-probe","cdp","browser-crash"],"backgroundTag":"broken-pipe","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}