spring-projects/spring-boot · error · IllegalStateException

Failed to load layers configuration with name

Error message

Failed to load layers configuration with name '%s': '%s' not found

What it means

Thrown by AbstractPackagerMojo.loadLayersConfigurationFromClasspath when the plugin's class realm cannot resolve the resource META-INF/spring/layers/<configurationName>.xml. The layers feature lets you split a fat jar into layers for better Docker caching; a named configuration must exist as a classpath resource at that exact location. A null InputStream means the resource is absent from every JAR/plugin dependency the realm can see.

Solutions

  1. Verify the exact resource path: the file must live at META-INF/spring/layers/<configurationName>.xml inside a JAR that the maven plugin can load (a plugin dependency, not just the application classpath).
  2. Correct the <configurationName> value to match the filename (without .xml) of an existing layers.xml.
  3. If using a custom layers file, bundle it in a dependency JAR listed under <plugin><dependencies> for spring-boot-maven-plugin, or drop the configurationName to fall back to IMPLICIT_LAYERS.
  4. Rebuild the artifact that is supposed to carry the resource and confirm with `jar tf <artifact>.jar | grep 'META-INF/spring/layers'` that the file is actually packaged.

Example fix

// before: <layers><configurationName>cunstom</configurationName></layers>
// after:  <layers><configurationName>custom</configurationName></layers>  // matches META-INF/spring/layers/custom.xml
Defensive patterns

Strategy: validation

Validate before calling

// Before configuring <layers><configurationName>NAME</configurationName></layers>:
String name = /* configured value */;
String loc = "META-INF/spring/layers/" + name + ".xml";
try (var in = Thread.currentThread().getContextClassLoader().getResourceAsStream(loc)) {
    if (in == null) {
        throw new IllegalStateException("Missing layers resource: " + loc
            + " — verify the file ships in a plugin dependency the realm can see.");
    }
}

Try / catch

// In a build-extension wrapper around the plugin goal:
try {
    // invoke repackage/build-image
} catch (IllegalStateException ex) {
    if (ex.getMessage().startsWith("Failed to load layers configuration")) {
        // surface a hint pointing at META-INF/spring/layers/<name>.xml
    }
    throw ex;
}

Prevention

When it happens

Trigger: Setting <layers><configurationName>foo</configurationName></layers> in the plugin config when no META-INF/spring/layers/foo.xml is present on the plugin descriptor's class realm (getResourceAsStream returns null). Triggered during repackage/build-image when layers are enabled and a configurationName is supplied.

Common situations: Typo in configurationName; resource shipped in the application JAR instead of a plugin dependency the realm sees; resource placed under src/main/resources of the app rather than the plugin; upgrading Spring Boot and the legacy layers.xml path moved; custom layer config only declared but never added to the build classpath.

Related errors


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

Appendix: source

Thrown at build-plugin/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractPackagerMojo.java:215

	}

	private org.springframework.boot.loader.tools.Layers loadLayersConfiguration() {
		File configuration = this.layers.getConfiguration();
		if (configuration != null) {
			return getCustomLayers(configuration.getAbsolutePath(), () -> new FileInputStream(configuration));
		}
		String configurationName = this.layers.getConfigurationName();
		if (configurationName != null) {
			String location = "META-INF/spring/layers/%s.xml".formatted(configurationName);
			return getCustomLayers(location, () -> loadLayersConfigurationFromClasspath(configurationName, location));
		}
		return IMPLICIT_LAYERS;
	}

	private InputStream loadLayersConfigurationFromClasspath(String name, String location) {
		InputStream in = this.pluginDescriptor.getClassRealm().getResourceAsStream(location);
		if (in == null) {
			throw new IllegalStateException(
					"Failed to load layers configuration with name '%s': '%s' not found".formatted(name, location));
		}
		return in;
	}

	private CustomLayers getCustomLayers(String source, InputStreamSource inputStreamSource) {
		try {
			Document document = getDocumentIfAvailable(inputStreamSource);
			return new CustomLayersProvider().getLayers(document);
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to process custom layers configuration " + source, ex);
		}
	}

	private Document getDocumentIfAvailable(InputStreamSource source) throws Exception {
		try (InputStream in = source.getInputStream()) {
			InputSource inputSource = new InputSource(in);

View on GitHub (pinned to 270dfe353f)