karatelabs/karate · error · RuntimeException
Timeout waiting for available driver for scenario
Error message
Timeout waiting for available driver for scenario '{scenarioName}' [{stats}] What it means
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.
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
Example fix
// before: 10 parallel threads, pool of 2
mvn test -Dtest=Runner -Dkarate.options="-t ~@wip" // threads=10, poolSize=2
// after: match concurrency to pool
System.setProperty("karate.env", "chrome");
runner.path("classpath:features").parallel(2); // threads == pool size
// or size the pool explicitly for 10 threads Defensive patterns
Strategy: validation
Validate before calling
// before running, ensure concurrency <= pool size
int threads = runnerThreads;
if (threads > driverPoolSize) {
throw new IllegalStateException("threads " + threads + " > poolSize " + driverPoolSize);
} Type guard
null
Try / catch
try { karate.run(scenarios); }
catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Timeout waiting for available driver")) {
logger.error("Pool stats: {} — reduce threads or increase pool", e.getMessage());
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Pool exhausted for scenario
- timed out waiting for promise
- Timeout waiting for Karate test events after
- Pool full, closing excess driver
- HTTP endpoint not available
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/3d35a0ed2308b202.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/driver/PooledDriverProvider.java:200
poolSize = runtime.getFeatureRuntime().getSuite().threadCount;
logger.info("Auto-detected pool size from Suite: {}", poolSize);
}
if (poolSize < 1) {
poolSize = 1; // Fallback
logger.warn("Could not detect pool size, defaulting to 1");
}
availableDrivers = new ArrayBlockingQueue<>(poolSize);
}
}
private Driver waitForDriver(Map<String, Object> config, ScenarioRuntime runtime) {
String scenarioName = runtime.getScenario().getName();
logger.warn("Pool exhausted for scenario '{}', waiting for available driver... [{}]",
scenarioName, getStats());
try {
Driver driver = availableDrivers.poll(30, TimeUnit.SECONDS);
if (driver == null) {
throw new RuntimeException("Timeout waiting for available driver for scenario '"
+ scenarioName + "' [" + getStats() + "]");
}
// If the driver is terminated OR fails reset/liveness, replace it.
// Same rationale as takeHealthyFromPool — a poisoned driver must not be
// handed out to the next scenario.
if (driver.isTerminated()) {
createdCount.decrementAndGet();
driver = createDriver(config);
createdCount.incrementAndGet();
logger.info("Replaced terminated driver for scenario: {}", scenarioName);
} else if (!resetDriver(driver)) {
closeDriverQuietly(driver);
createdCount.decrementAndGet();
driver = createDriver(config);
createdCount.incrementAndGet();
logger.info("Replaced unhealthy driver for scenario: {}", scenarioName);
} else {
logger.info("Acquired pooled driver after wait for scenario: {}", scenarioName);View on GitHub (pinned to a22eb90246)