Skip to content

Add extract support for executable war files #51367

Closed as not planned
Closed as not planned

Description

@ttreuthardt

Summary

Running the tools jarmode extract command against an executable Spring Boot WAR fails with IllegalStateException: Invalid library location WEB-INF/lib-provided/.... The extractor assumes every entry listed in WEB-INF/classpath.idx lives under the single Spring-Boot-Lib directory (WEB-INF/lib/), but an executable WAR legitimately also places the provided / embedded-container dependencies under WEB-INF/lib-provided/ and records them in the classpath index. The first lib-provided entry encountered aborts the command.

Environment

  • Spring Boot: 4.1.0
  • Packaging: WAR (executable, WarLauncher)
  • Java (runtime): 25
  • Build: Maven WAR Plugin 3.5.1

Steps to reproduce

A minimal project generated from start.spring.io reproduces the problem.
The key factors are WAR packaging plus the web dependency, which places the embedded Tomcat and spring-boot-web-server artifacts in WEB-INF/lib-provided/.

  1. Generate a project (Maven, Java, WAR packaging, Java 25, Spring Boot 4.1.0,
    web dependency):

    https://start.spring.io/#!type=maven-project&language=java&platformVersion=4.1.0&packaging=war&configurationFileFormat=properties&jvmVersion=25&groupId=com.example&artifactId=demo&packageName=com.example.demo&dependencies=web

  2. Build the WAR: ./mvnw package

  3. Run the tools jarmode extract command: java -Djarmode=tools -jar target/demo-0.0.1-SNAPSHOT.war extract

The command fails with the stack trace shown below.

Actual behavior

java.lang.IllegalStateException: java.lang.IllegalStateException: Invalid library location WEB-INF/lib-provided/spring-boot-web-server-4.1.0.jar
        at org.springframework.boot.jarmode.tools.ToolsJarMode.run(ToolsJarMode.java:58)
        at org.springframework.boot.loader.launch.JarModeRunner.runJarMode(JarModeRunner.java:64)
        at org.springframework.boot.loader.launch.JarModeRunner.main(JarModeRunner.java:44)
        ...
Caused by: java.lang.IllegalStateException: Invalid library location WEB-INF/lib-provided/spring-boot-web-server-4.1.0.jar
        at org.springframework.util.Assert.state(Assert.java:102)
        at org.springframework.boot.jarmode.tools.IndexedJarStructure.toStructureDependency(IndexedJarStructure.java:133)
        at org.springframework.boot.jarmode.tools.IndexedJarStructure.resolve(IndexedJarStructure.java:102)
        at org.springframework.boot.jarmode.tools.JarStructure.resolve(JarStructure.java:40)
        at org.springframework.boot.jarmode.tools.ExtractCommand.lambda$extractLibraries$0(ExtractCommand.java:162)
        ...

Expected behavior

extract completes and lays out the archive contents, treating WEB-INF/lib-provided/ entries as libraries (or otherwise handling them) instead of asserting on the Spring-Boot-Lib prefix.

Analysis

The WAR is internally consistent. Manifest, WEB-INF/layers.idx, and WEB-INF/classpath.idx all agree that WEB-INF/lib-provided/ exists and is on the runtime classpath. Relevant manifest attributes:

Spring-Boot-Version: 4.1.0
Spring-Boot-Lib: WEB-INF/lib/
Spring-Boot-Classpath-Index: WEB-INF/classpath.idx
Spring-Boot-Layers-Index: WEB-INF/layers.idx
Main-Class: org.springframework.boot.loader.launch.WarLauncher

During extraction, IndexedJarStructure.resolve(String) classifies every archive entry.
Any entry present in classpathEntries (loaded verbatim from classpath.idx) is treated as a library and passed to toStructureDependency, which asserts on the Spring-Boot-Lib prefix:

private String toStructureDependency(String location) {
    Assert.state(location.startsWith(this.libLocation),   // libLocation = "WEB-INF/lib/"
            () -> "Invalid library location " + location);
    return location.substring(this.libLocation.length());
}

Our context

We are transitioning from traditional WAR deployments to running Spring Boot applications with the embedded Tomcat. During this transition we need to keep producing WAR files, while still using the tools jarmode to build layered container images. Hence, we would welcome a fix that allows tools extract to work with executable WARs as well.

Activity

  1. changed the title [-]jarmode=tools extract fails on executable WAR with scope provided dependencies[/-] [+]Add extract support for executable WAR files[/+] on Aug 12, 2026
  2. added and removed
    status: waiting-for-triageAn issue that has not yet been triaged. Contributions will not be accepted.
    on Aug 12, 2026
  3. added this to the 4.x milestone on Aug 12, 2026
  4. changed the title [-]Add extract support for executable WAR files[/-] [+]Add extract support for executable war files[/+] on Aug 12, 2026
  5. mhalbritter commented on Aug 12, 2026

    @mhalbritter
    Contributor

    Hey,

    thanks for the report. The extract command only works with jar files at the moment. This issue tracks the work on adding it for the war files, too.

  6. exijn commented on Aug 26, 2026

    @exijn

    Hi, I'd be interested in working on this issue.

    I plan to first add a focused regression test that builds an executable WAR with entries in both WEB-INF/lib/ and WEB-INF/lib-provided/, then runs the tools jarmode extract command and verifies the extracted layout.

    I’ll review how IndexedJarStructure currently maps classpath entries and look for the smallest extension that supports WAR library locations without changing the existing executable JAR behavior.

    Would this scope and direction be suitable for a contribution?

  7. mhalbritter commented on Aug 26, 2026

    @mhalbritter
    Contributor

    Hello @hej090224 thanks for the offer, but we already have a PR for this: #51372

  8. mhalbritter commented on Aug 26, 2026

    @mhalbritter
    Contributor

    Superseded by #51372.

  9. added
    status: supersededAn issue that has been superseded by another
    and removed on Aug 26, 2026
  10. removed this from the 4.x milestone on Aug 26, 2026
  11. added a commit that references this issue on Aug 26, 2026
    40e4e83
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: supersededAn issue that has been superseded by another

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions