apache/flink · warning · IOException

operation interrupted

Error message

operation interrupted

What it means

guardIfWindows retries file operations on Windows inside a loop because Windows throws AccessDeniedException while a file is still mmap'ed or held open by another process (a known JVM/OS quirk). Between retries it sleeps 1ms; if that sleep is interrupted, the method restores the thread's interrupt flag and throws IOException("operation interrupted"). It is a control-flow error: the deletion/delete operation was aborted because the calling thread was interrupted, not because of an I/O failure.

Source

Thrown at flink-core/src/main/java/org/apache/flink/util/FileUtils.java:410

    // or  make this locking more fine grained, for example  on directory path prefixes
    private static void guardIfWindows(ThrowingConsumer<File, IOException> toRun, File file)
            throws IOException {
        synchronized (DELETE_LOCK) {
            for (int attempt = 1; attempt <= 10; attempt++) {
                try {
                    toRun.accept(file);
                    break;
                } catch (AccessDeniedException e) {
                    // ah, windows...
                }

                // briefly wait and fall through the loop
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    // restore the interruption flag and error out of the method
                    Thread.currentThread().interrupt();
                    throw new IOException("operation interrupted");
                }
            }
        }
    }

    // Guard Mac for the same reason we guard windows. Refer to guardIfWindows for details.
    // The difference to guardIfWindows is that we don't swallow the AccessDeniedException because
    // doing that would lead to wrong behaviour.
    private static void guardIfMac(ThrowingConsumer<File, IOException> toRun, File file)
            throws IOException {
        synchronized (DELETE_LOCK) {
            toRun.accept(file);
        }
    }

    /**
     * Copies all files from source to target and sets executable flag. Paths might be on different
     * systems.

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Let the interrupted thread exit gracefully: since the interrupt flag is already restored, catch the IOException, check Thread.currentThread().isInterrupted(), and skip re-attempting the delete
  2. Avoid interrupting threads that perform file cleanup — use shutdown() instead of shutdownNow(), or run cleanup on a non-interruptible path
  3. Retry the deletion once on a fresh (non-interrupted) thread if the cleanup must complete

Example fix

// before
executor.shutdownNow(); // interrupts cleanup thread -> IOException("operation interrupted")

// after
executor.shutdown();
executor.awaitTermination(30, TimeUnit.SECONDS);
Defensive patterns

Strategy: try-catch

Try / catch

try {
    FileUtils.deleteFileOrDirectory(file);
} catch (IOException e) {
    if (Thread.currentThread().isInterrupted()) { /* aborted by cancel: stop cleanup */ return; }
    throw e;
}

Prevention

When it happens

Trigger: Running on Windows (or macOS with the analogous guard) and interrupting the thread executing FileUtils.deleteFileOrDirectory/deleteDirectory — e.g. cancelling a job, shutting down an executor, or a timeout-based Future cancellation while local file cleanup is in progress.

Common situations: Local Flink runs on Windows developers' machines where cleanup tasks run in a thread pool that gets shutdownNow() (which interrupts workers) during job cancellation or JVM shutdown hooks.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/2b9dfddbe552a987. Report an issue: GitHub.