spring-projects/spring-boot · error · RuntimeException

Failed to get file mode from CopySpec

Error message

Failed to get file mode from CopySpec

What it means

Thrown by BootArchiveSupport.getFileMode, the reflective counterpart to getDirMode. It invokes CopySpec.getFileMode() via reflection on the legacy path (Gradle < 8.3). The caller selects between the modern Property<ConfigurableFilePermissions> API (asUnixNumeric) and this reflective fallback based on the Gradle version. Any reflection exception is wrapped and rethrown.

Solutions

  1. Upgrade Gradle to 8.3+ to use filePermissions().toUnixNumeric() directly.
  2. Ensure no custom plugin is replacing or wrapping the CopySpec type used by bootJar/bootWar.
  3. Downgrade the Spring Boot plugin to match an old Gradle if upgrade is impossible.

Example fix

// before: Gradle 7.x
// after: upgrade to Gradle 8.14+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
Defensive patterns

Strategy: validation

Validate before calling

// Ensure Gradle 8.3+ before boot archive tasks.
if (GradleVersion.current().compareTo(GradleVersion.version("8.3")) < 0) {
    throw new GradleException("Upgrade to Gradle 8.3+ to avoid reflective getFileMode.")
}

Prevention

When it happens

Trigger: Building a boot archive on Gradle < 8.3 where CopySpec lacks a getFileMode() method or the method throws during invocation. The caller first tries asUnixNumeric(copySpec.getFilePermissions()); if that path is not taken, it falls through to getFileMode(copySpec).

Common situations: Identical root causes to getDirMode failures: old Gradle version, custom CopySpec subclass, or a Gradle distribution where the method was removed. Often co-occurs with error [5].

Related errors


AI-assisted analysis of spring-projects/spring-boot@270dfe353f (2026-08-11). Data as JSON: /api/errors/3d5cdfb97d0ef678. Report an issue: GitHub.

Appendix: source

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

	private @Nullable Integer asUnixNumeric(Property<ConfigurableFilePermissions> permissions) {
		return permissions.isPresent() ? permissions.get().toUnixNumeric() : null;
	}

	private @Nullable Integer getDirMode(CopySpec copySpec) {
		try {
			return (Integer) copySpec.getClass().getMethod("getDirMode").invoke(copySpec);
		}
		catch (Exception ex) {
			throw new RuntimeException("Failed to get dir mode from CopySpec", ex);
		}
	}

	private @Nullable Integer getFileMode(CopySpec copySpec) {
		try {
			return (Integer) copySpec.getClass().getMethod("getFileMode").invoke(copySpec);
		}
		catch (Exception ex) {
			throw new RuntimeException("Failed to get file mode from CopySpec", ex);
		}
	}

	private boolean isUsingDefaultLoader(Jar jar) {
		return DEFAULT_LAUNCHER_CLASSES.contains(jar.getManifest().getAttributes().get("Main-Class"));
	}

	void requiresUnpack(String... patterns) {
		this.requiresUnpack.include(patterns);
	}

	void requiresUnpack(Spec<FileTreeElement> spec) {
		this.requiresUnpack.include(spec);
	}

	void excludeNonZipLibraryFiles(FileCopyDetails details) {
		if (this.librarySpec.isSatisfiedBy(details)) {
			excludeNonZipFiles(details);

View on GitHub (pinned to 270dfe353f)