🔴 The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
// DOC-DERIVED — verifier output shape stable since Java 7; location/reason blocks vary by offender:
java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
com/devinhyderabad/report/AuditLogger.log(Ljava/lang/String;)V @12: invokevirtual
Reason:
Type 'java/lang/String' (current frame, stack[1]) is not assignable to 'int'
Current Frame:
flags: { }
locals: { 'com/devinhyderabad/report/AuditLogger', 'java/lang/String' }
stack: { 'com/devinhyderabad/report/AuditLogger', 'java/lang/String', 'int' }⚡ Quick Fix Works 80% of the time
Identify the class named in Location, find which agent or generator touches it, then upgrade or disable that instrumenter and rebuild the artifact.
# run once with zero agents to prove the app itself is clean
java -jar app.jar
# bisect agents one by one
java -javaagent:jacoco.jar -jar app.jar # VerifyError returns? upgrade jacoco🧠 Why this Happens
Tap to expand the deep technical explanation
Before executing any method the verifier simulates every branch, tracking the type of each operand-stack slot against a proof built from StackMapTable frames embedded since class-file version 50. If an instruction pops a String where an int must live, or a branch target lacks a valid frame, verification aborts before main runs. Hand-written ASM transformers, obfuscators, AOP weavers, and outdated agents emit frames their authors computed by hand — and get them subtly wrong. The class never runs a single instruction; rejection happens at load/link time.
The HITEC City Parking Spot Analogy:
Airport security inspects every suitcase by X-ray before boarding. One bag contains a battery packed where liquids belong — the whole suitcase is refused at the gate, no matter how fine the contents actually are.
🔁 How to Reproduce Confirm this is your error
Attach an old instrumentation agent (or weave a class with hand-written ASM emitting inconsistent frames), then start the JVM. Verification fires during loading, before any application output. DOC-DERIVED — producing honest VerifyErrors requires deliberately malformed bytecode.
🛠️ Solutions (5 Ways to Fix)
Bisect agents and weavers until the offender is isolated
👉 Use this whenever VerifyError appears only under profilers, coverage tools, AOP, or -javaagent flags.
Start the app with no agents, then attach candidates one at a time. The first run that reproduces the error names the guilty instrumenter; upgrading it to a JDK-matching release usually ends the story.
java -jar app.jar # baseline: clean?
java -javaagent:aspectjweaver.jar -jar app.jar
java -javaagent:jacocoagent.jar -jar app.jar # repeat per agentUpgrade the bytecode toolkit to match your JDK class-file version
👉 Use this if you own code generating or transforming classes with ASM, Byte Buddy, or Javassist.
Each JDK ships a new class-file format version; older libraries cannot compute frames for what they have never seen. Pin versions known-good for the running JDK (ASM 9.x for recent releases).
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.7</version>
</dependency>Let ASM recompute frames instead of hand-writing them
👉 Use this if your own transformer produces the invalid frames.
ClassWriter.COMPUTE_FRAMES makes ASM derive StackMapTable entries from a simulated dataflow, eliminating the manual arithmetic that breaks verification. Pass the original ClassReader into the writer so ASM can copy constant-pool context while recomputing frames.
ClassReader reader = new ClassReader(bytes);
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
// transform with a ClassVisitor accepting 'reader' and writing into 'writer'
byte[] verified = writer.toByteArray();Re-download or rebuild the corrupt artifact
👉 Use this when the named class should never have been transformed at all.
Truncated jars and partially written target/classes can also fail structural checks. Purge caches, rebuild clean, and compare checksums against the repository metadata.
mvn clean install
rm -rf ~/.m2/repository/com/devinhyderabad/report && mvn -U dependency:resolve
sha256sum target/app.jar # compare with CI artifactDisable bytecode verification wholesale
👉 DEV ONLY — never ship this; see below.
DEV ONLY. The flags still launch the JVM on every JDK tested (17 and 25 print a deprecation warning and continue), which makes them seductive — but they turn off the safety net that stops memory-corrupting bytecode, so production JVMs and security policy treat them as handing the keys to whatever jar lands on the classpath next. Use solely to confirm the diagnosis locally; plan for the day the option is deleted.
# DEV ONLY - diagnosis only, absolutely not for deployment
java -Xverify:none -jar app.jar
# OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify
# were deprecated in JDK 13 and will likely be removed in a future release.📋 Version Notes
StackMapTable mandatory since Java 7; most legacy-agent VerifyErrors originate here.
Unchanged rules; more frameworks began weaving, raising exposure.
-Xverify:none / -noverify deprecated with a startup warning (still functional).
Flags still accepted with the same warning (verified on 17.0.19 and 25.0.2) — de facto dead, formally pending removal.
🛡️ How to Prevent This Next Time
Keep the agent/toolchain matrix pinned and tested on every JDK bump, never hand-compute stack maps, and smoke-boot the fully instrumented artifact in CI so verification failures surface before release.