spring-projects/spring-boot · error · RuntimeException

Failed to read main class name from '{}'

Error message

Failed to read main class name from '{}'

What it means

Thrown by ResolveMainClassName.ClassNameReader.transform() when Files.readString on the main-class-name output file raises IOException, wrapped as a RuntimeException (note: the cause is not chained). It indicates the file that resolveMainClassName previously wrote could not be read back when the provider is queried lazily.

Source

Thrown at build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ResolveMainClassName.java:184

		private final String classpath;

		private ClassNameReader(String classpath) {
			this.classpath = classpath;
		}

		@Override
		public String transform(RegularFile file) {
			if (file.getAsFile().length() == 0) {
				throw new InvalidUserDataException(
						"Main class name has not been configured and it could not be resolved from classpath "
								+ this.classpath);
			}
			Path output = file.getAsFile().toPath();
			try {
				return Files.readString(output);
			}
			catch (IOException ex) {
				throw new RuntimeException("Failed to read main class name from '" + output + "'");
			}
		}

	}

}

View on GitHub (pinned to 5b2dbdbb8b)

Solutions

  1. Run a clean build: ./gradlew clean bootJar to regenerate the main-class-name file.
  2. Ensure no external process (IDE, antivirus, another Gradle daemon) removes files under build/ during the build.
  3. Avoid sharing a single build directory across parallel Gradle invocations.
  4. If reproducible, inspect the file path in the message and check filesystem permissions/disk health.

Example fix

// shell: force regeneration of the resolveMainClassName output
//   ./gradlew clean resolveMainClassName bootJar
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the resolveMainClassName output exists and is readable before consuming
val outFile = layout.buildDirectory.file("resolvedMainClassName").get().asFile
if (!outFile.exists() || outFile.length() == 0L) {
    throw GradleException("resolveMainClassName output missing; run :resolveMainClassName")
}

Try / catch

try {
    val mainClass = tasks.named<ResolveMainClassName>("resolveMainClassName").get().readMainClassName().get()
} catch (ex: RuntimeException) {
    if (ex.message?.startsWith("Failed to read main class name from") == true) {
        logger.lifecycle("Re-running resolveMainClassName to regenerate output")
        tasks.named("resolveMainClassName").get().actions.forEach { /* re-trigger */ }
    }
    throw ex
}

Prevention

When it happens

Trigger: readMainClassName() maps outputFile through ClassNameReader; at line 181 Files.readString(output) fails (file deleted mid-build, moved, permission denied, FS error), producing the RuntimeException at line 184.

Common situations: Another task or external process deleted build/ intermediate files between resolveMainClassName and the consumer task; concurrent Gradle builds sharing a build dir; antivirus/IDE file locking on Windows; running with --parallel and a cleaned build dir; stale build after an interrupted build.

Related errors


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