karatelabs/karate · warning
timeout waiting for target to be removed
Error message
timeout waiting for target to be removed: {} What it means
After closing a tab, CdpDriver waits for Target.targetDestroyed (or the page list to shrink) and falls back to a direct getPages() check. If the target still appears present after the wait, this warning is logged — the target removal event never arrived. Usually benign if the target did go away but the event/page list lagged; concerning if the tab genuinely failed to close.
Solutions
- Ignore if the tab is actually gone — this is a warning, not an error; verify with driver.getPages().
- Remove beforeunload handlers in the page before closing tabs.
- If targets genuinely leak, close the whole browser (quit) instead of individual tabs.
- Check CDP/websocket health and browser version; upgrade if event delivery is chronically delayed.
Example fix
// before
driver.close(); // closes current tab, may warn
// after
driver.script("window", "window.onbeforeunload = null");
driver.close(); Defensive patterns
Strategy: fallback
Validate before calling
// remove beforeunload before closing tabs
driver.script("window", "window.onbeforeunload = null"); Try / catch
try {
driver.close();
} catch (RuntimeException e) {
// if the target genuinely leaked, tear down the whole browser
if (!driver.isTerminated()) { driver.quit(); }
} Prevention
- Clear beforeunload handlers before tab close
- Prefer quitting the whole browser when closing many tabs
- Verify with getPages() whether the target truly leaked
- Keep browser builds current to avoid event-delivery delays
When it happens
Trigger: close() of a tab where Target.targetDestroyed wasn't observed within the wait window, slow CDP event delivery under load, beforeunload handlers blocking close, or a hung renderer keeping the target alive.
Common situations: Closing tabs with beforeunload dialogs, heavily loaded CI machines delaying CDP events, pages with long-running JS blocking teardown, or browser builds with delayed target cleanup.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- failed to create blank tab, quitting driver
- CDP connection failed readiness check
- CDP timeout for
- CDP error
- dialog accept failed
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/c37948f136a86855.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/driver/cdp/CdpDriver.java:2802
/**
* Wait for a closed target to actually disappear, event-driven: the future is
* completed by the Target.targetDestroyed handler. That event's delivery depends
* on Target.setDiscoverTargets, which initialize() arms best-effort — so on
* timeout, fall back to ONE page-list check before declaring a problem.
*/
private void waitForTargetRemoved(String targetId, CompletableFuture<Void> removed) {
try {
removed.get(1000, TimeUnit.MILLISECONDS);
logger.trace("target removal confirmed by event: {}", targetId);
return;
} catch (Exception e) {
// fall through to the direct check
}
if (!getPages().contains(targetId)) {
logger.trace("target removed from page list: {}", targetId);
return;
}
logger.warn("timeout waiting for target to be removed: {}", targetId);
}
public boolean isTerminated() {
return terminated;
}
@Override
public boolean isReady() {
CompletableFuture<Integer> f = mainContextReady;
return f.isDone() && !f.isCompletedExceptionally();
}
@Override
public void waitUntilReady() {
// The main frame execution context being live is the authoritative readiness
// signal (see mainContextReady). Block on it up to the configured timeout;
// returns immediately when already ready.
awaitMainContext(options.getTimeoutDuration().toMillis());View on GitHub (pinned to a22eb90246)