In this tutorial, we’ll look at the cause of java.lang.VerifyError errors and multiple ways to avoid it.
The Java Virtual Machine (JVM) distrusts all loaded bytecode as a core tenet of the Java Security Model. During runtime, the JVM will load .class files and attempt to link them together to form an executable — but the validity of these loaded .class files is unknown.
To ensure that the loaded .class files do not pose a threat to the final executable, the JVM performs verification on the .class files. Additionally, the JVM ensures that binaries are well-formed. For example, the JVM will verify classes do not subtype final classes.
In many cases, verification fails on valid, non-malicious bytecode because a newer version of Java has a stricter verification process than older versions. For example, JDK 13 may have added a verification step that was not enforced in JDK 7. Thus, if we run an application with JVM 13 and include dependencies compiled with an older version of the Java Compiler (javac), the JVM may consider the outdated dependencies to be invalid.
Thus, when linking older .class files with a newer JVM, the JVM may throw a java.lang.VerifyError similar to the following:
java.lang.VerifyError: Expecting a stackmap frame at branch target X
com/example/baeldung.Foo(Lcom/example/baeldung/Bar:Baz;)Lcom/example/baeldung/Foo; @1: infonull
Expected stackmap frame at this location.
0000000: 0001 0002 0003 0004 0005 0006 0007 0008
0000010: 0001 0002 0003 0004 0005 0006 0007 0008
There are two ways to solve this problem:
- Update dependencies to versions compiled with an updated javac
- Disable Java verification
3. Production Solution
The most common cause of a verification error is linking binaries using a newer JVM version compiled with an older version of javac. This is more common when dependencies have bytecode generated by tools such as Javassist, which may have generated outdated bytecode if the tool is outdated.
To resolve this issue, update dependencies to a version built using a JDK version that matches the JDK version used to build the application. For example, if we build an application using JDK 13, the dependencies should be built using JDK 13.
To find a compatible version, inspect the Build-Jdk in the JAR Manifest file of the dependency to ensure it matches the JDK version used to build the application.
4. Debugging & Development Solution
When debugging or developing an application, we can disable verification as a quick-fix.
Do not use this solution for production code.
By disabling verification, the JVM can link malicious or faulty code to our applications, resulting in security compromises or crashes when executed.
Also note that as of JDK 13, this solution has been deprecated, and we should not expect this solution to work in future Java releases. Disabling verification will result in the following warning:
Java HotSpot(TM) 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated
in JDK 13 and will likely be removed in a future release.
The mechanism for disabling bytecode verification varies based on how we run our code.
4.1. Command Line
To disable verification on the command line, pass the noverify flag to the java command:
java -noverify Foo.class
Note that -noverify is a shortcut for-Xverify:none and both can be used interchangeably.
To disable verification in a Maven build, pass the noverify flag to any desired plugin:
<!-- ... -->
<!-- ... -->
To disable verification in a Gradle build, pass the noverify flag to any desired task:
jvmArgs = jvmArgs << "-noverify"
In this quick tutorial, we learned why the JVM performs bytecode verification and what causes the java.lang.VerifyError error. We also explored two solutions: A production one and a non-production one.
When possible, use the latest versions of dependencies rather than disabling verification.