karatelabs/karate · error · RuntimeException

waitForOutput timed out after ms

Error message

waitForOutput timed out after ms

What it means

waitForOutput(predicate, timeout) registers a predicate over the process output and waits for a line/chunk matching it. If the deadline passes with no matching output, it removes the pending predicate/future and throws 'waitForOutput timed out after <n>ms'. It means the expected output never appeared in the process's stdout/stderr within the timeout.

Solutions

  1. Verify the exact expected text against the process's actual output (capture getStdOut()/getStdErr() after failure)
  2. Increase the timeout; first-run JIT/classpath startup on CI is much slower than locally
  3. Ensure the target output stream is captured - keep redirectErrorStream true (default) or attach a listener to stderr too
  4. Use readyLine() for readiness instead of ad-hoc substring matching, or loosen the predicate (case-insensitive/regex)

Example fix

// before
handle.waitForOutput("Server is ready", 5_000); // banner actually is "Listening on..."
// after
handle.waitForOutput("Listening on", 30_000); // or readyLine():
handle.readyLine(30_000);
Defensive patterns

Strategy: try-catch

Validate before calling

if (timeoutMillis < expectedStartupUpperBoundMs) {
    throw new IllegalArgumentException("waitForOutput timeout " + timeoutMillis + "ms below expected startup bound");
}

Try / catch

try {
    handle.waitForOutput("Started", 30_000);
} catch (RuntimeException e) {
    if (e.getMessage().startsWith("waitForOutput timed out")) {
        log.error("process output so far: stdout={} stderr={}", handle.getStdOut(), handle.getStdErr());
        handle.stop(5_000);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling waitForOutput("some text", timeoutMillis) (or the no-timeout overload's delegates, jsGet, readyLine) when the process never prints matching output - wrong expected string, slower startup than the timeout, or output going to a stream not captured (e.g. with redirectErrorStream false and text on stderr).

Common situations: Waiting for a server 'started' banner that changed across versions, matching against stderr while only stdout is wired to listeners, fixed timeouts too small for cold CI runners, or typos/case mismatches in the expected substring.

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.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/bfe02ca70be92ea8. Report an issue: GitHub.

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/process/ProcessHandle.java:402

     *
     * @param predicate     Test function that receives the line
     * @param timeoutMillis Timeout in milliseconds (0 = no timeout)
     * @return The line that matched
     */
    public String waitForOutput(Predicate<String> predicate, long timeoutMillis) {
        CompletableFuture<String> future = new CompletableFuture<>();
        waitPredicates.add(predicate);
        waitFutures.put(predicate, future);
        try {
            if (timeoutMillis > 0) {
                return future.get(timeoutMillis, TimeUnit.MILLISECONDS);
            } else {
                return future.get();
            }
        } catch (TimeoutException e) {
            waitPredicates.remove(predicate);
            waitFutures.remove(predicate);
            throw new RuntimeException("waitForOutput timed out after " + timeoutMillis + "ms");
        } catch (Exception e) {
            waitPredicates.remove(predicate);
            waitFutures.remove(predicate);
            throw new RuntimeException("error in waitForOutput", e);
        }
    }

    public String getStdOut() {
        synchronized (stdoutBuffer) {
            return stdoutBuffer.toString();
        }
    }

    public String getStdErr() {
        synchronized (stderrBuffer) {
            return stderrBuffer.toString();
        }
    }

View on GitHub (pinned to a22eb90246)