elastic/elasticsearch · error · IOException
File still exists after {} waits.
Error message
File still exists after {} waits. What it means
Thrown inside deleteWithRetry0 when, after MAX_RETRY_DELETE_TIMES attempts (15 on Windows, 0 elsewhere), Files.notExists(path) is still false — the file survived every delete call. This is a Windows-specific retry path; on Unix the retry loop is effectively disabled (0 iterations) so this throw is Windows-only in practice.
Source
Thrown at build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java:1262
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (InterruptedException x) {
throw new UncheckedIOException("Interrupted while deleting.", new IOException());
}
}
// The exception handling here is loathsome, but necessary!
private void deleteWithRetry0(Path path) throws IOException, InterruptedException {
int times = 0;
IOException ioe = null;
while (true) {
try {
fileSystemOperations.delete(d -> d.delete(path));
times++;
// Checks for absence of the file. Semantics of Files.exists() is not the same.
while (Files.notExists(path) == false) {
if (times > MAX_RETRY_DELETE_TIMES) {
throw new IOException("File still exists after " + times + " waits.");
}
Thread.sleep(RETRY_DELETE_MILLIS);
// retry
fileSystemOperations.delete(d -> d.delete(path));
times++;
}
break;
} catch (NoSuchFileException ignore) {
// already deleted, ignore
break;
} catch (org.gradle.api.UncheckedIOException | IOException x) {
if (x.getCause() instanceof NoSuchFileException) {
// already deleted, ignore
break;
}
// Backoff/retry in case another process is accessing the file
times++;
if (ioe == null) ioe = new IOException();View on GitHub (pinned to db6a809a66)
Solutions
- Add antivirus exclusions for the gradle build / testclusters directory on Windows.
- Ensure all child processes (ML, watchers) are stopped before teardown — check the node's children list in stopHandle.
- Close any file explorer / IDE that has the data dir open.
- As a last resort, manually delete the leftover path under build/testclusters and re-run.
Defensive patterns
Strategy: retry
Prevention
- On Windows, add antivirus exclusions for the gradle build/testclusters directory.
- Ensure all child processes (ML, watchers) are stopped before teardown.
- Close any file explorer / IDE window that has the data dir open.
When it happens
Trigger: fileSystemOperations.delete succeeds without throwing but the file remains on disk (or reappears), and Files.notExists stays false for 15 attempts spaced 500ms apart (total ~7.5s). Classic Windows behavior where a file handle is held by another process.
Common situations: Antivirus or search indexer holds a handle on a shard/lock file under the testcluster data dir; a child ML/monitoring process is still dying and holds the file; Windows file-delete semantics where the file is marked for deletion but not gone until the last handle closes.
Related errors
- Interrupted while deleting.
- Failed to tail log {}
- Number of nodes should be >= 1 but was {} for {}
- Cannot shrink {} to have {} nodes as it already has {}
- Cannot add nodes to test cluster after is has been frozen
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/01e60f402406ec9e.
Report an issue: GitHub.