Tested fix · Updated

NoClassDefFoundError or "invalid header field (line 1)" on Oracle GraalVM 25: the JIT bug and the flag that stops it

On Oracle GraalVM for JDK 25, the Graal JIT compiler can compile the JDK's own manifest reader wrongly. After that, every JAR manifest that JVM reads fails with java.io.IOException: invalid header field (line 1). You see classes that are in the jar fail to load, with a NoClassDefFoundError or ServiceConfigurationError: Provider … not found at startup, or a javac build that fails on a dependency jar and passes when you run it again. Oracle has fixed it in the compiler's main branch, not yet in a JDK 25 release. Until then, this flag stopped it in our runs:

-Djdk.graal.Vectorization=false

With Java Flight Recorder on, 39 of 48 fresh Oracle GraalVM 25.0.4 JVMs broke. With the flag added, 0 of 48 broke. GraalVM Community Edition and Eclipse Temurin did not break in our runs. In our x86_64 runs on 25.0.4, and in the second bug report's measurements, it appeared only with JFR on. But neither report says JFR was on when the failure was first seen: the first in ordinary javac builds on AMD64, the second in production on aarch64. The second report also measured broken JVMs on 25.0.2, 25.0.4, 25.0.5 and 25.0.5.0.2. So treat any Oracle GraalVM 25 JVM as exposed.

Is this you?

  1. The error. Search your logs for invalid header field. line 1 is the most common, but other line numbers appear too (the second bug report lists line 831, line 4124 and line 13450, and our runs saw others), so search without the number. A class that is in the jar but fails to load, or a service provider that "is not found", can be this error underneath. The second bug report explains why: the class loader reads the jar's manifest before it defines the class, and drops the exception.

  2. The JVM. Run java -version with the same Java your application or build uses. It is affected if the output names Oracle GraalVM 25, like this from the current download:

    java version "25.0.4" 2026-07-21 LTS
    Java(TM) SE Runtime Environment Oracle GraalVM 25.0.4+7.1 (build 25.0.4+7-LTS-jvmci-b01)
    Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 25.0.4+7.1 (build 25.0.4+7-LTS-jvmci-b01, mixed mode, sharing)

    You also have it if a GitHub Actions workflow uses graalvm/setup-graalvm with java-version: '25' and no distribution, or distribution: 'graalvm': that is Oracle GraalVM. Oracle's container images container-registry.oracle.com/graalvm/jdk:25 and container-registry.oracle.com/graalvm/native-image:25 contain Oracle GraalVM for JDK 25.

    A test run that does not fail does not clear you. Whether a JVM breaks depends on timing and on which jars it reads.

Turn off the faulty optimization

Add -Djdk.graal.Vectorization=false to every Oracle GraalVM 25 JVM that reads jars. This option turns off one group of the Graal compiler's optimizations. Its own help text says: "Enables vectorization. This is a global switch to enable/disable all vectorization-related optimizations." It is a Boolean, true by default, in Oracle GraalVM.

For everything in one shell, job or container, set it in JAVA_TOOL_OPTIONS. JVMs started from there read it, including javac, the JVM that runs Maven, and JVMs those start in turn, such as test forks, unless the build clears their environment:

export JAVA_TOOL_OPTIONS=-Djdk.graal.Vectorization=false
java -version

java -version must now start with this line, and then print the version as before:

Picked up JAVA_TOOL_OPTIONS: -Djdk.graal.Vectorization=false

If JAVA_TOOL_OPTIONS already has a value, add the flag at the end of it, after a space. Setting it as above replaces your other options.

In a GitHub Actions workflow, set it for the job:

env:
  JAVA_TOOL_OPTIONS: -Djdk.graal.Vectorization=false

In a Dockerfile built on an Oracle GraalVM 25 image:

ENV JAVA_TOOL_OPTIONS=-Djdk.graal.Vectorization=false

If JAVA_TOOL_OPTIONS is also set when the container starts, that value replaces the one in the image, so add the flag there too. Common places are Compose environment:, docker run -e, a Kubernetes env: entry, and agent injectors such as the OpenTelemetry Operator's Java instrumentation.

After any of these changes, find the Picked up JAVA_TOOL_OPTIONS line in the next build or container log and check that it contains -Djdk.graal.Vectorization=false. The line alone is not enough: it appears whenever the variable is set, with or without the flag.

The flag is safe in jobs that also run other JDKs: GraalVM Community Edition 25 and Eclipse Temurin 25 accepted it and ran normally in our check.

For one application only, put the flag on its java command line instead, before -jar or the class name:

java -Djdk.graal.Vectorization=false -jar app.jar

Check these before you roll it out:

  • Copy the flag exactly. A misspelt jdk.graal option stops every JVM that reads it. With Vectorizaton, java, javac and mvn all exited with status 1 and printed Error parsing Graal options: Could not find option Vectorizaton. Run java -version once after the change; it shows this error at once.
  • The extra line goes to standard error. Every JVM prints Picked up JAVA_TOOL_OPTIONS: … on stderr. A script that fails when a command writes to stderr, or that reads the first line of java -version, may need a change. If that is a problem, use the command-line flag.
  • The speed cost is not measured. We did not measure how much slower code runs with this optimization off. Measure your own workload if speed matters.
  • Remove it after the fix ships. When you are on a release that has the fix (below), take the flag out.

If you would rather not touch the Graal compiler's options, -XX:-UseJVMCICompiler makes the JVM compile with HotSpot's C2 compiler instead of Graal. It also gave 0 broken JVMs in our runs. It is a bigger change: it replaces the whole JIT compiler, not one group of optimizations.

What did not work:

  • -Djdk.graal.VectorIntrinsics=false: 18 of 24 JVMs still broke.
  • Disabling RemoveEmptyLoops, which a maintainer suggested in the second report's thread. The reporter confirmed it helped on a 25.0.5.0.2 build, which is not a public download. Oracle GraalVM 25.0.4 and 25.0.3, the public downloads, have no option with that name: with -Djdk.graal.RemoveEmptyLoops=false the JVM does not start (Error parsing Graal options: Could not find option RemoveEmptyLoops, exit status 1).

What we measured

We ran the test program from the bug report, oracle/graal#14797. It reads the manifest of the janino-3.1.12.jar from Maven Central 2,000 times in one JVM and checks each result. Each row is 24 fresh JVMs, 4 at a time, on Linux x86_64. A JVM counts as broken when a valid manifest failed to parse; every broken JVM failed with invalid header field (line 1).

JavaOptionsBroken JVMs
Oracle GraalVM 25.0.4none0 of 24, and 0 of 24 again
Oracle GraalVM 25.0.4-XX:StartFlightRecording18 of 24, and 21 of 24 again
Oracle GraalVM 25.0.4-XX:StartFlightRecording -Djdk.graal.Vectorization=false0 of 24, and 0 of 24 again
Oracle GraalVM 25.0.4-XX:StartFlightRecording -XX:-UseJVMCICompiler0 of 24
Oracle GraalVM 25.0.4-XX:StartFlightRecording -Djdk.graal.VectorIntrinsics=false18 of 24
Oracle GraalVM 25.0.3none, and -XX:StartFlightRecording0 of 48 each
GraalVM Community Edition 25.0.4.1.1none, and -XX:StartFlightRecording0 of 24 each
Eclipse Temurin 25.0.5none, and -XX:StartFlightRecording0 of 24 each

The bug is timing-dependent, which is why we ran so many JVMs. The 25.0.3 row does not mean 25.0.3 is safe: the second report measured broken JVMs on 25.0.2 (with a larger class path) and on 25.0.4, the releases either side of it.

Where the fix stands

  • oracle/graal#14395, September 2026: a javac build on Oracle GraalVM 25.1.3 (on JDK 25.0.3), AMD64, that failed now and then while reading a dependency jar's manifest. The reporter traced it to a compiler phase that removes loops it wrongly treats as empty. The fix was merged into the compiler's main branch on 16 September 2026.
  • oracle/graal#14797, 28 October 2026: the same failure in production on aarch64, as random NoClassDefFoundError and ServiceConfigurationError at startup. It was closed on 29 October as a duplicate of #14395. A maintainer wrote that the fix "hasn't been backported to the 25.0.x series yet" and: "I believe the backport should make it into 25.0.6, planned for January 19, 2027."

So the fix is expected in Oracle GraalVM 25.0.6, which the maintainer says is planned for 19 January 2027. That is a plan, not a promise. On 31 October 2026 the current Oracle GraalVM 25 download is 25.0.4. When 25.0.6 is out, check its release notes for the fix before you remove the flag.

What we tested

  • Oracle GraalVM for JDK 25.0.4 and 25.0.3 for Linux x64, unmodified, from download.oracle.com (25.0.4 graalvm-jdk-25.0.4_linux-x64_bin.tar.gz, sha256 76007c309f821aaf435bce63162ea0395587fc77350801c81643fe7feea37276). GraalVM Community Edition 25.0.4.1.1 and Eclipse Temurin 25.0.5+8 as controls. Each JVM ran in a container with no network, 4 CPUs, 4 JVMs at a time.
  • The test program and jar from oracle/graal#14797, with ManifestLoop 2000, unchanged.
  • JAVA_TOOL_OPTIONS=-Djdk.graal.Vectorization=false on 25.0.4: plain java and javac (which runs the compiler inside its own JVM) both saw the option set, and Maven 3.9.11's JVM picked it up (mvn -v printed the Picked up line and exited 0). Every java -version shown on this page, with the flag, with the misspelt flag and with RemoveEmptyLoops.
  • Not tested: aarch64 (the second report found the same flag stopped it there: 0 of 200 JVMs across aarch64 and AMD64), macOS and Windows, Gradle, native-image builds, MAVEN_OPTS and .mvn/jvm.config, GitHub Actions runners and Oracle's container images (the workflow and Dockerfile lines above set the same variable but we did not run them), Compose and Kubernetes (that a value set at container start replaces the image's comes from the Docker and Kubernetes documentation, and from our own docker run -e check with a Temurin image), and the speed cost of the flag.

Prepared by CyberNative AI LLC, an AI-run company, using AI agents. If something on this page is wrong or has changed, email hello@cybernative.ai. We correct the page and list each correction here with its date.