{"record":{"id":"3d35a0ed2308b202","repo":"karatelabs/karate","slug":"timeout-waiting-for-available-driver-for-scenario","errorCode":null,"errorMessage":"Timeout waiting for available driver for scenario '{scenarioName}' [{stats}]","messagePattern":"Timeout waiting for available driver for scenario '(.+?)' \\[(.+?)\\]","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"karate-core/src/main/java/io/karatelabs/driver/PooledDriverProvider.java","lineNumber":200,"sourceCode":"                poolSize = runtime.getFeatureRuntime().getSuite().threadCount;\n                logger.info(\"Auto-detected pool size from Suite: {}\", poolSize);\n            }\n            if (poolSize < 1) {\n                poolSize = 1;  // Fallback\n                logger.warn(\"Could not detect pool size, defaulting to 1\");\n            }\n            availableDrivers = new ArrayBlockingQueue<>(poolSize);\n        }\n    }\n\n    private Driver waitForDriver(Map<String, Object> config, ScenarioRuntime runtime) {\n        String scenarioName = runtime.getScenario().getName();\n        logger.warn(\"Pool exhausted for scenario '{}', waiting for available driver... [{}]\",\n                scenarioName, getStats());\n        try {\n            Driver driver = availableDrivers.poll(30, TimeUnit.SECONDS);\n            if (driver == null) {\n                throw new RuntimeException(\"Timeout waiting for available driver for scenario '\"\n                        + scenarioName + \"' [\" + getStats() + \"]\");\n            }\n            // If the driver is terminated OR fails reset/liveness, replace it.\n            // Same rationale as takeHealthyFromPool — a poisoned driver must not be\n            // handed out to the next scenario.\n            if (driver.isTerminated()) {\n                createdCount.decrementAndGet();\n                driver = createDriver(config);\n                createdCount.incrementAndGet();\n                logger.info(\"Replaced terminated driver for scenario: {}\", scenarioName);\n            } else if (!resetDriver(driver)) {\n                closeDriverQuietly(driver);\n                createdCount.decrementAndGet();\n                driver = createDriver(config);\n                createdCount.incrementAndGet();\n                logger.info(\"Replaced unhealthy driver for scenario: {}\", scenarioName);\n            } else {\n                logger.info(\"Acquired pooled driver after wait for scenario: {}\", scenarioName);","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-core/src/main/java/io/karatelabs/driver/PooledDriverProvider.java#L182-L218","documentation":"When all drivers in the pool are busy, waitForDriver blocks on availableDrivers.poll(30s). If no driver is returned within the 30-second timeout, this RuntimeException is thrown with the scenario name and pool statistics. It means the pool is smaller than concurrent demand or drivers are stuck/leaked.","triggerScenarios":"More concurrent scenarios than poolSize drivers; a driver never released (missing release() for a ScenarioRuntime); drivers permanently busy/hung so the queue stays empty for >30 seconds while acquire() waits.","commonSituations":"karate.options/threads set higher than the auto-detected pool size; a previous scenario crashed without releasing its driver (leak); browser hangs (network stalls, unresponsive page) keeping drivers checked out; heavy parallel CI with limited machine resources.","solutions":["Reduce the number of parallel Karate threads to be <= the driver pool size","Increase the pool size (poolSize / auto-detected size) to match or exceed runner concurrency","Find and fix driver leaks: ensure every scenario releases its driver even on failure (proper teardown hooks)","Investigate hung scenarios/drivers that hold a driver indefinitely; add scenario timeouts","Check getStats() in the message to confirm whether drivers are all assigned or the pool never initialized"],"exampleFix":"// before: 10 parallel threads, pool of 2\nmvn test -Dtest=Runner -Dkarate.options=\"-t ~@wip\" // threads=10, poolSize=2\n// after: match concurrency to pool\nSystem.setProperty(\"karate.env\", \"chrome\");\nrunner.path(\"classpath:features\").parallel(2); // threads == pool size\n// or size the pool explicitly for 10 threads","handlingStrategy":"validation","validationCode":"// before running, ensure concurrency <= pool size\nint threads = runnerThreads;\nif (threads > driverPoolSize) {\n    throw new IllegalStateException(\"threads \" + threads + \" > poolSize \" + driverPoolSize);\n}","typeGuard":"null","tryCatchPattern":"try { karate.run(scenarios); }\ncatch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Timeout waiting for available driver\")) {\n        logger.error(\"Pool stats: {} — reduce threads or increase pool\", e.getMessage());\n    } else throw e;\n}","preventionTips":["Keep runner parallelism <= driver pool size","Always release drivers in teardown so the queue refills","Watch getStats() warnings ('Pool exhausted') and tune capacity before timeouts hit","Set scenario timeouts so hung scenarios cannot pin drivers forever"],"tags":["driver-pool","timeout","concurrency"],"backgroundTag":"request-timeout","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}