elastic/elasticsearch · warning · UncheckedIOException

Failed to tail log {}

Error message

Failed to tail log {}

What it means

Thrown while reading the ES output log file (esOutputFile) in logFileContents after the cluster stops, when an IOException escapes the LineNumberReader and tailLogs is true. tailLogs is true when the caller explicitly requested the full log (e.g. on failure) or when leak-detector lines were detected. The plugin fails loudly here because losing the diagnostic log on a failing run would hide the real error.

Source

Thrown at build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java:1156

                                Pair.of(
                                    ring.getLast(), // Original, non-normalized message (so we keep the first timestamp)
                                    ofNullable(errorsAndWarnings.get(normalizedMessage)).map(p -> p.right() + 1).orElse(1)
                                )
                            );
                        }
                    } else {
                        // We combine multi line log messages to make sure we never break exceptions apart
                        lineToAdd = ring.removeLast() + "\n" + line;
                    }
                }
                ring.add(lineToAdd);
                if (ring.size() >= TAIL_LOG_MESSAGES_COUNT) {
                    ring.removeFirst();
                }
            }
        } catch (IOException e) {
            if (tailLogs) {
                throw new UncheckedIOException("Failed to tail log " + this, e);
            }
            return;
        }

        boolean foundLeaks = false;
        for (String logLine : errorsAndWarnings.keySet()) {
            if (logLine.contains("ResourceLeakDetector") || logLine.contains("LeakTracker")) {
                tailLogs = true;
                foundLeaks = true;
                break;
            }
        }
        if (tailLogs) {
            if (errorsAndWarnings.isEmpty() == false || ring.isEmpty() == false) {
                LOGGER.lifecycle("\n=== {} `{}` ===", description, this);
            }
            if (errorsAndWarnings.isEmpty() == false) {
                LOGGER.lifecycle("\n»    ↓ errors and warnings from " + from + " ↓");

View on GitHub (pinned to db6a809a66)

Solutions

  1. Check that no parallel gradle task or external process is deleting the testclusters working directory during the run.
  2. If on Windows, ensure antivirus exclusions for the gradle build dir; file locking by AV is a common cause.
  3. Run the test in isolation (--tests <one>) to rule out concurrency between clusters sharing a working dir root.
  4. If the file was legitimately gone, re-run: this is often transient and the log will be present next time.
Defensive patterns

Strategy: try-catch

Validate before calling

if (Files.exists(from) == false) {
    LOGGER.warn("Log file {} gone before tail; skipping", from);
    return;
}

Try / catch

try (LineNumberReader reader = new LineNumberReader(Files.newBufferedReader(from))) {
    ...
} catch (IOException e) {
    if (tailLogs) throw new UncheckedIOException("Failed to tail log " + this, e);
    return;
}

Prevention

When it happens

Trigger: Files.newBufferedReader(from) or reader.readLine() throws — the log file was deleted, moved, or its permissions changed between cluster start and the post-stop log sweep. On Windows the file may still be locked by the just-killed JVM.

Common situations: A concurrent cleanup task (reaper, gradle clean) deleted the working dir while the log was being tailed; the working dir is on a tmpfs that got cleared; antivirus quarantined the log; or a previous run's file handle is still open on Windows.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/da539fdd28824b88. Report an issue: GitHub.