spring-projects/spring-boot · error · GradleException

Failed to add {} to {}

Error message

Failed to add {} to {}

What it means

Thrown by BootZipCopyAction.Processor.process() when writing a single FileCopyDetails into the archive raises IOException, wrapped as GradleException naming both the entry (details) and the target archive. It is per-entry granularity: the message tells you exactly which file could not be added to which archive.

Source

Thrown at build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java:222

			this.layerIndex = (BootZipCopyAction.this.layerResolver != null)
					? new LayersIndex(BootZipCopyAction.this.layerResolver.getLayers()) : null;
		}

		void process(FileCopyDetails details) {
			if (skipProcessing(details)) {
				return;
			}
			try {
				writeLoaderEntriesIfNecessary(details);
				if (details.isDirectory()) {
					processDirectory(details);
				}
				else {
					processFile(details);
				}
			}
			catch (IOException ex) {
				throw new GradleException("Failed to add " + details + " to " + BootZipCopyAction.this.output, ex);
			}
		}

		private boolean skipProcessing(FileCopyDetails details) {
			return BootZipCopyAction.this.exclusions.isSatisfiedBy(details)
					|| (this.writtenLoaderEntries != null && this.writtenLoaderEntries.isWrittenDirectory(details));
		}

		private void processDirectory(FileCopyDetails details) throws IOException {
			String name = details.getRelativePath().getPathString();
			ZipArchiveEntry entry = new ZipArchiveEntry(name + '/');
			prepareEntry(entry, name, getTime(details), getDirMode(details));
			this.out.putArchiveEntry(entry);
			this.out.closeArchiveEntry();
			this.writtenDirectories.add(name);
		}

		private void processFile(FileCopyDetails details) throws IOException {

View on GitHub (pinned to 5b2dbdbb8b)

Solutions

  1. Inspect the message for the specific entry path and verify that file is readable and present.
  2. Run ./gradlew clean to ensure inputs are regenerated from scratch.
  3. If the failing entry is a dependency jar, re-resolve dependencies (--refresh-dependencies) to replace a corrupt cached artifact.
  4. Check for locked/missing source files (IDE/OS-level) and free disk space.

Example fix

// shell: reproduce with clean + refresh deps
//   ./gradlew clean bootJar --refresh-dependencies --info
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check that all inputs to bootJar are readable
sourceSets.main.get().output.files.forEach { d ->
    require(d.canRead()) { "Cannot read source dir ${d.absolutePath}" }
}
configurations.runtimeClasspath.get().files.forEach { f ->
    require(f.canRead()) { "Cannot read dependency ${f.absolutePath}" }
}

Try / catch

try {
    tasks.named("bootJar").get().copy()
} catch (ex: org.gradle.api.GradleException) {
    if (ex.message?.startsWith("Failed to add ") == true) {
        logger.error("A specific entry could not be added; see the file named in the message.")
    }
    throw ex
}

Prevention

When it happens

Trigger: For each entry, process() writes loader/directory/file; if details.copyTo(out) or putArchiveEntry/closeArchiveEntry throws IOException, line 222 wraps it with 'Failed to add <details> to <output>'.

Common situations: A source file vanishes between scanning and copying (mid-build deletion); a corrupted/locked input jar; oversized entry; filesystem errors on the build volume; an input dependency is unreadable.

Related errors


AI-assisted analysis of spring-projects/spring-boot@5b2dbdbb8b (2026-08-04). Data as JSON: /data/errors/78da5af1948daf8d.json. Report an issue: GitHub.