karatelabs/karate · error · RuntimeException

retry interrupted

Error message

retry interrupted

What it means

While sleeping the configured retry interval between `retry until` attempts, the worker thread received an interrupt (InterruptedException). Karate re-asserts the interrupt flag and throws this RuntimeException wrapping the InterruptedException, aborting the retry loop.

Solutions

  1. Let the scenario fail; the interrupt means something upstream wants the thread to stop — investigate the source of the cancellation.
  2. Avoid wrapping Karate scenarios in very short outer timeouts that interrupt mid-retry.
  3. If interruption is expected during shutdown, catch and treat it as cancellation rather than a test failure.
  4. Reduce retry counts/intervals so retries complete before any outer timeout fires.

Example fix

// before (runner)
executor.setAwaitTerminationMillis(1000); // interrupts mid-retry
// after
executor.setAwaitTerminationMillis(60000); // give retry sleeps room to finish
Defensive patterns

Strategy: try-catch

Try / catch

// Wrap retry-heavy scenarios so interruption is distinguishable
try {
    Result r = karate.run(path);
} catch (RuntimeException e) {
    if (e.getMessage().equals("retry interrupted") && Thread.currentThread().isInterrupted()) {
        // expected during shutdown; skip reporting as product failure
        return;
    }
    throw e;
}

Prevention

When it happens

Trigger: A step in `retry until` execution was sleeping between attempts when the thread was interrupted — typically by test-suite shutdown, a JUnit/TestNG timeout, an executor shutdown, or manual cancellation.

Common situations: Suite timeouts killing a hung scenario mid-retry; CI pipeline cancellation; shutting down an embedded runner while a retry sleep is in flight.

Related errors


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

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/core/StepExecutor.java:2342

    }

    private HttpResponse executeMethodWithRetry(String method, String retryUntil, KarateConfig config) {
        int maxRetries = config.getRetryCount();
        int sleepInterval = config.getRetryInterval();
        int retryCount = 0;
        Suite suite = getSuite();

        while (true) {
            if (retryCount == maxRetries) {
                throw new RuntimeException("retry failed after " + maxRetries + " attempts: " + retryUntil);
            }
            if (retryCount > 0) {
                try {
                    logger.debug("sleeping {} ms before retry #{}", sleepInterval, retryCount);
                    Thread.sleep(sleepInterval);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("retry interrupted", e);
                }
            }

            // Make a copy of the request builder to preserve state for retry
            HttpRequestBuilder httpCopy = http().copy();

            // Build request for HTTP_ENTER event (method already set in doMethod)
            HttpRequest request = http().build();

            // Fire HTTP_ENTER event - listener can return false to skip
            boolean shouldProceed = true;
            if (suite != null) {
                shouldProceed = suite.fireEvent(HttpRunEvent.enter(request, runtime));
            }

            HttpResponse response;
            if (shouldProceed) {
                response = http().invoke(method);

View on GitHub (pinned to a22eb90246)