karatelabs/karate · warning
Pool full, closing excess driver
Error message
Pool full, closing excess driver
What it means
On release(), the driver is offered back to the ArrayBlockingQueue of available drivers. If the queue is already full (offered == false), the excess driver is closed quietly and createdCount is decremented. The code notes this 'shouldn't happen with correct sizing', so it usually indicates a bookkeeping bug or duplicate release.
Solutions
- Ensure each acquired driver is released exactly once (check for duplicate release in error/teardown paths)
- Keep poolSize stable for the JVM lifetime; do not shrink queue capacity after drivers are created
- Check getStats()/createdCount logs to spot leaked or double-released drivers
Defensive patterns
Strategy: validation
Validate before calling
// track acquire/release pairing in your harness
Set<Driver> outstanding = ConcurrentHashMap.newKeySet();
// on acquire: outstanding.add(d); on release: if (!outstanding.remove(d)) log.warn("double release"); Prevention
- Release each driver exactly once; centralize teardown
- Do not shrink poolSize after drivers are created
- Monitor getStats()/createdCount for drift
When it happens
Trigger: Calling release() (or acquire/release lifecycle) more times than the pool expects, or pool size shrunk at runtime so queue capacity < outstanding drivers.
Common situations: A scenario runtime released twice on error paths; misconfigured poolSize smaller than number of drivers created earlier in the JVM; mixed use of pooled and non-pooled providers.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Timeout waiting for available driver for scenario
- Pool exhausted for scenario
- Provider has been shut down
- Interrupted while waiting for driver
- error in waitForOutput
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/d9552fe698f8aaad.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/driver/PooledDriverProvider.java:284
// scenario that acquires this driver.
//
// Why this matters (context for future maintainers): intercept.feature's
// wildcard-pattern scenario set Fetch.enable via driver.intercept() and never
// called stopIntercept. The driver went back to the pool with Fetch.enable
// still active and a stale JS InterceptHandler from a destroyed scenario.
// Every subsequent Page.navigate on that driver paused ALL subresource
// requests through our onRequestPaused event handler, which runs synchronously
// on the CDP websocket dispatch thread. A heavy page (HTML + CSS + JS + etc)
// could saturate that thread, queueing the Page.navigate command response
// behind the Fetch event backlog. The 30s CDP wall-clock timeout then fired
// before the response was dequeued → "CDP timeout for: Page.navigate".
cleanScenarioState(driver);
// Return to pool
boolean offered = availableDrivers.offer(driver);
if (!offered) {
// Pool full (shouldn't happen with correct sizing)
logger.warn("Pool full, closing excess driver");
closeDriverQuietly(driver);
createdCount.decrementAndGet();
} else {
logger.debug("Returned driver to pool for scenario: {}", runtime.getScenario().getName());
}
}
/**
* Tear down scenario-scoped driver state at owner-scenario exit. Called from
* {@link #release} before the driver is offered back to the pool.
* <p>
* Only state that belongs to the scenario's scope should be cleaned here —
* NOT cookies or page state, those belong in {@link #resetDriver} at acquire
* time (per-scenario setup vs per-scenario teardown).
* </p>
* <p>
* Subclasses overriding this should call {@code super.cleanScenarioState(driver)}
* to preserve intercept/dialog teardown.View on GitHub (pinned to a22eb90246)