{"record":{"id":"cec2f9a92aa32a5f","repo":"oracle/graal","slug":"unbalanced-monitors-locked-objects-do-not-match","errorCode":null,"errorMessage":"unbalanced monitors - locked objects do not match","messagePattern":"unbalanced monitors - locked objects do not match","errorType":"exception","errorClass":"PermanentBailoutException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/FrameStateBuilder.java","lineNumber":511,"sourceCode":"\n        if (rethrowException != other.rethrowException) {\n            throw new GraalError(incompatibilityErrorMessage(\"mismatch in rethrowException flag\", other));\n        }\n\n        if (stackSize() != other.stackSize()) {\n            throw new GraalError(incompatibilityErrorMessage(\"mismatch in stack sizes\", other));\n        }\n        for (int i = 0; i < stackSize(); i++) {\n            ValueNode x = stack[i];\n            ValueNode y = other.stack[i];\n            assert x != null;\n            assert y != null;\n            if (x != y && (x == TWO_SLOT_MARKER || x.isDeleted() || y == TWO_SLOT_MARKER || y.isDeleted() || x.getStackKind() != y.getStackKind())) {\n                throw new GraalError(incompatibilityErrorMessage(\"mismatch in stack types\", other));\n            }\n        }\n        if (lockedObjects.length != other.lockedObjects.length) {\n            throw new PermanentBailoutException(incompatibilityErrorMessage(\"unbalanced monitors - locked objects do not match\", other));\n        }\n        for (int i = 0; i < lockedObjects.length; i++) {\n            if (monitorIds[i] != other.monitorIds[i]) {\n                if (MonitorIdNode.monitorIdentityEquals(monitorIds[i], other.monitorIds[i])) {\n                    continue;\n                }\n                throw new PermanentBailoutException(incompatibilityErrorMessage(\"unbalanced monitors - monitors do not match\", other));\n            }\n        }\n    }\n\n    public boolean areLocksMergeableWith(FrameStateBuilder other) {\n        if (lockedObjects.length != other.lockedObjects.length) {\n            return false;\n        }\n        for (int i = 0; i < lockedObjects.length; i++) {\n            if (!MonitorIdNode.monitorIdentityEquals(monitorIds[i], other.monitorIds[i])) {\n                return false;","sourceCodeStart":493,"sourceCodeEnd":529,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/FrameStateBuilder.java#L493-L529","documentation":"Thrown by FrameStateBuilder when two control-flow predecessors reaching a merge point hold different numbers of locked monitors (lockedObjects.length differs). Bytecode requires that at every merge point the monitor stack be identical; a mismatch means the method enters/exits monitors unevenly across paths, which the verifier would reject, so Graal permanently bails out.","triggerScenarios":"FrameStateBuilder merge compatibility check finding this.lockedObjects.length != other.lockedObjects.length at a control-flow merge — e.g. one branch holding a monitor the other does not (monitorenter without matching monitorexit on all paths).","commonSituations":"Hand-written or generated bytecode with asymmetric synchronized blocks; obfuscators rewriting try/finally monitor exit sequences; broken monitorenter/monitorexit pairing inserted by instrumentation agents; class files that fail strict verification.","solutions":["Verify the class (`-Xverify:all`) to confirm the monitor imbalance and locate the method.","Fix the bytecode/source so every path through the region enters and exits the same monitors (proper try/finally or synchronized blocks).","Update or reconfigure the bytecode tool (obfuscator/agent) whose transformation broke monitor pairing."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// verify monitor pairing at merge points\n// `java -Xverify:all` rejects monitorenter/exit imbalance before Graal ever sees it","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair monitorenter/monitorexit in a try/finally shape in generated bytecode.","Use `synchronized` blocks in source instead of manual monitor bytecode."],"tags":["graalvm","bytecode-verification","monitors","merge","bailout"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}