gradle/gradle · error · UncheckedIOException

Failed to clean up stale outputs

Error message

Failed to clean up stale outputs

What it means

Before executing tasks, Gradle deletes stale regular files left in task output locations (leftovers from previous runs, or outputs of a task that declared overlapping outputs). StaleOutputCleaner.cleanupOutputs wraps any IOException raised while deleting one of those files into an UncheckedIOException with this message, so the build aborts with the underlying filesystem error as the cause.

Source

Thrown at platforms/software/platform-base/src/main/java/org/gradle/language/base/internal/tasks/StaleOutputCleaner.java:78

        OutputsCleaner outputsCleaner = new OutputsCleaner(
            deleter,
            file -> {
                String absolutePath = file.getAbsolutePath();
                return prefixes.stream()
                    .anyMatch(absolutePath::startsWith);
            },
            dir -> !directoriesToClean.contains(dir)
        );

        try {
            for (File f : filesToDelete) {
                if (f.isFile()) {
                    outputsCleaner.cleanupOutput(f, FileType.RegularFile);
                }
            }
            outputsCleaner.cleanupDirectories();
        } catch (IOException e) {
            throw new UncheckedIOException("Failed to clean up stale outputs", e);
        }

        return outputsCleaner.getDidWork();
    }

    @CheckReturnValue
    public static boolean cleanEmptyOutputDirectories(Deleter deleter, Iterable<File> directories, File directoryToClean) {
        return cleanEmptyOutputDirectories(deleter, directories, ImmutableSet.of(directoryToClean));
    }

    @CheckReturnValue
    public static boolean cleanEmptyOutputDirectories(Deleter deleter, Iterable<File> directories, Collection<File> directoriesToClean) {
        OutputsCleaner outputsCleaner = new OutputsCleaner(
            deleter,
            file -> false,
            dir -> !directoriesToClean.contains(dir)
        );

View on GitHub (pinned to 534f27719b)

Solutions

  1. Run with --stacktrace and read the caused-by IOException to get the exact file path and OS-level reason
  2. Stop competing processes: gradle --stop for other daemons, close applications/IDEs using build outputs, then re-run
  3. Fix permissions/ownership so the build user can delete files under the output directories (chown -R, correct Docker volume uid)
  4. If outputs sit on a read-only or full filesystem, free space or relocate outputs (buildDir, --project-cache-dir)
  5. Remove overlapping output-directory declarations between tasks so stale-output cleanup is not needed between them
Defensive patterns

Strategy: try-catch

Validate before calling

def outDirs = tasks.withType(JavaCompile).collect { it.destinationDirectory.get().asFile }
outDirs.each { d ->
    assert d.isDirectory() && d.canWrite() : "Output dir not writable: $d"
    def probe = new File(d, '.delete-probe')
    probe.text = 'x'
    assert probe.delete() : "Cannot delete files in $d - a lock or permission will break stale-output cleanup"
}

Try / catch

try {
    // via Tooling API: buildLauncher.forTasks('build').run()
    org.gradle.tooling.GradleConnector.newConnector()
        .forProjectDirectory(new File('.'))
        .connect().newBuild().forTasks('build').run()
} catch (Exception e) {
    def root = e
    while (root.cause != null && !(root.cause instanceof java.io.IOException)) root = root.cause
    if (root.message?.contains('Failed to clean up stale outputs')) {
        // release locks: gradle --stop, close apps, then retry once
    } else { throw e }
}

Prevention

When it happens

Trigger: StaleOutputCleaner.cleanupOutputs iterates filesToDelete and calls outputsCleaner.cleanupOutput(f, FileType.RegularFile) inside the try block. Any IOException from that call - a file locked by another process, permission denied on the file or a parent directory, or a read-only filesystem - escapes the loop and is rethrown as UncheckedIOException('Failed to clean up stale outputs', e).

Common situations: Windows build hosts where antivirus, an indexer, or a running IDE holds a lock on a JAR/class file under build/; a second Gradle daemon or a still-running application writing into the same build directory; CI containers where the workspace volume is owned by a different uid; NFS/SMB mounts that deny deletes; full disks.

Related errors


AI-assisted analysis of gradle/gradle@534f27719b (2026-08-22). Data as JSON: /api/errors/1f61401e8fa7e061. Report an issue: GitHub.