{"record":{"id":"1f61401e8fa7e061","repo":"gradle/gradle","slug":"failed-to-clean-up-stale-outputs","errorCode":null,"errorMessage":"Failed to clean up stale outputs","messagePattern":"Failed to clean up stale outputs","errorType":"exception","errorClass":"UncheckedIOException","httpStatus":null,"severity":"error","filePath":"platforms/software/platform-base/src/main/java/org/gradle/language/base/internal/tasks/StaleOutputCleaner.java","lineNumber":78,"sourceCode":"        OutputsCleaner outputsCleaner = new OutputsCleaner(\n            deleter,\n            file -> {\n                String absolutePath = file.getAbsolutePath();\n                return prefixes.stream()\n                    .anyMatch(absolutePath::startsWith);\n            },\n            dir -> !directoriesToClean.contains(dir)\n        );\n\n        try {\n            for (File f : filesToDelete) {\n                if (f.isFile()) {\n                    outputsCleaner.cleanupOutput(f, FileType.RegularFile);\n                }\n            }\n            outputsCleaner.cleanupDirectories();\n        } catch (IOException e) {\n            throw new UncheckedIOException(\"Failed to clean up stale outputs\", e);\n        }\n\n        return outputsCleaner.getDidWork();\n    }\n\n    @CheckReturnValue\n    public static boolean cleanEmptyOutputDirectories(Deleter deleter, Iterable<File> directories, File directoryToClean) {\n        return cleanEmptyOutputDirectories(deleter, directories, ImmutableSet.of(directoryToClean));\n    }\n\n    @CheckReturnValue\n    public static boolean cleanEmptyOutputDirectories(Deleter deleter, Iterable<File> directories, Collection<File> directoriesToClean) {\n        OutputsCleaner outputsCleaner = new OutputsCleaner(\n            deleter,\n            file -> false,\n            dir -> !directoriesToClean.contains(dir)\n        );\n","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/gradle/gradle/blob/534f27719b66953f95cc907aae7f2c1b12f5482d/platforms/software/platform-base/src/main/java/org/gradle/language/base/internal/tasks/StaleOutputCleaner.java#L60-L96","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Run with --stacktrace and read the caused-by IOException to get the exact file path and OS-level reason","Stop competing processes: gradle --stop for other daemons, close applications/IDEs using build outputs, then re-run","Fix permissions/ownership so the build user can delete files under the output directories (chown -R, correct Docker volume uid)","If outputs sit on a read-only or full filesystem, free space or relocate outputs (buildDir, --project-cache-dir)","Remove overlapping output-directory declarations between tasks so stale-output cleanup is not needed between them"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"def outDirs = tasks.withType(JavaCompile).collect { it.destinationDirectory.get().asFile }\noutDirs.each { d ->\n    assert d.isDirectory() && d.canWrite() : \"Output dir not writable: $d\"\n    def probe = new File(d, '.delete-probe')\n    probe.text = 'x'\n    assert probe.delete() : \"Cannot delete files in $d - a lock or permission will break stale-output cleanup\"\n}","typeGuard":null,"tryCatchPattern":"try {\n    // via Tooling API: buildLauncher.forTasks('build').run()\n    org.gradle.tooling.GradleConnector.newConnector()\n        .forProjectDirectory(new File('.'))\n        .connect().newBuild().forTasks('build').run()\n} catch (Exception e) {\n    def root = e\n    while (root.cause != null && !(root.cause instanceof java.io.IOException)) root = root.cause\n    if (root.message?.contains('Failed to clean up stale outputs')) {\n        // release locks: gradle --stop, close apps, then retry once\n    } else { throw e }\n}","preventionTips":["Run a single Gradle daemon per project; run gradle --stop when switching Gradle versions","Exclude build/ directories from antivirus real-time scanning on Windows build hosts","Ensure the CI user owns the workspace volume in containerized builds","Never declare overlapping output directories between two tasks"],"tags":["io","file-system","task-outputs","cleanup","gradle"],"backgroundTag":"cannot-delete-locked-file","analyzedSha":"534f27719b66953f95cc907aae7f2c1b12f5482d","analyzedAt":"2026-08-22T08:09:12.375Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}