apache/iceberg · error · RuntimeException

Interrupted while running parallel task

Error message

Interrupted while running parallel task

What it means

If the current thread is interrupted while waiting for parallel task futures to complete, ParallelIterable throws a RuntimeException with this message, preserving the InterruptedException as the cause. Interruption usually means the surrounding job is being cancelled or shut down.

Solutions

  1. Inspect e.getCause() (InterruptedException) and restore the interrupt flag if you catch it
  2. Avoid long-lived ParallelIterable usage inside code that swallows interrupts
  3. Shut down executors gracefully instead of forcing cancellation

Example fix

// before
iterable.forEach(x -> process(x)); // interrupted mid-iteration
// after
try {
  iterable.forEach(x -> process(x));
} catch (RuntimeException e) {
  if (e.getCause() instanceof InterruptedException) Thread.currentThread().interrupt();
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (Thread.currentThread().isInterrupted()) { throw new IllegalStateException("already interrupted; skip parallel iteration"); }

Try / catch

catch (RuntimeException e) { if (e.getCause() instanceof InterruptedException) { Thread.currentThread().interrupt(); } throw e; }

Prevention

When it happens

Trigger: Cancelling a Spark job or shutting down the executor service while hasNext()/checkTasks() is blocked on Future.get of a ParallelIterable task.

Common situations: Job cancellation, query timeouts killing tasks, executor shutdown during parallel scan planning.

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 apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/6061a32bbf9abda3. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/util/ParallelIterable.java:167

      boolean hasRunningTask = false;

      for (int i = 0; i < taskFutures.length; i += 1) {
        if (taskFutures[i] == null || taskFutures[i].isDone()) {
          if (taskFutures[i] != null) {
            // check for task failure and re-throw any exception. Enqueue continuation if any.
            try {
              Optional<Task<T>> continuation = taskFutures[i].get();
              continuation.ifPresent(yieldedTasks::addLast);
              taskFutures[i] = null;
            } catch (ExecutionException e) {
              if (e.getCause() instanceof RuntimeException) {
                // rethrow a runtime exception
                throw (RuntimeException) e.getCause();
              } else {
                throw new RuntimeException("Failed while running parallel task", e.getCause());
              }
            } catch (InterruptedException e) {
              throw new RuntimeException("Interrupted while running parallel task", e);
            }
          }

          // submit a new task if there is space in the queue
          if (queue.size() < maxQueueSize) {
            taskFutures[i] = submitNextTask();
          }
        }

        if (taskFutures[i] != null) {
          hasRunningTask = true;
        }
      }

      return !closed.get() && (tasks.hasNext() || hasRunningTask);
    }

    private CompletableFuture<Optional<Task<T>>> submitNextTask() {

View on GitHub (pinned to 86d9c8fc54)