oracle/graal · error · PermanentBailoutException

At %s, bci %s: opcode %s (%s) has a stack effect of %s, the

Error message

At %s, bci %s: opcode %s (%s) has a stack effect of %s, the stack size is %s + %s, this will underflow the bytecode stack.

What it means

FrameStateBuilder.check JDK26-style stack-usage verifier: before an opcode is processed, it computes stackSize + pushedSlotCount + stackEffectOf(opcode) and permanently bails out if the result is negative — the instruction would pop more values than exist on the operand stack (stack underflow). Well-formed, verified bytecode never triggers this; hitting it means the bytecode is malformed or the frame state tracking diverged from reality.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/FrameStateBuilder.java:440

            return;
        }
        if (rethrowException) {
            /*
             * We seem to be inside explicit exception handling code, with the operation's inputs
             * already popped off the stack and an exception pushed instead. We can't recover the
             * original stack size for verification.
             */
            return;
        }

        int pushedSlotCount = 0;
        if (pushedSlotKinds != null) {
            for (JavaKind pushedKind : pushedSlotKinds) {
                pushedSlotCount += pushedKind.getSlotCount();
            }
        }
        if (stackSize + pushedSlotCount + Bytecodes.stackEffectOf(opcode) < 0) {
            throw new PermanentBailoutException("At %s, bci %s: opcode %s (%s) has a stack effect of %s, the stack size is %s + %s, this will underflow the bytecode stack.",
                            code, bci, opcode & 0xff, Bytecodes.nameOf(opcode), Bytecodes.stackEffectOf(opcode), stackSize, pushedSlotCount);
        }
    }

    public NodeSourcePosition createBytecodePosition(int bci) {
        BytecodeParser parent = parser.getParent();
        NodeSourcePosition position = create(bci, parent);
        return position;
    }

    private NodeSourcePosition create(int bci, BytecodeParser parent) {
        if (outerSourcePosition == null && parent != null) {
            outerSourcePosition = parent.getFrameStateBuilder().createBytecodePosition(parent.bci());
        }
        if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) {
            return FrameState.toSourcePosition(outerFrameState);
        }
        if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Run the class through a strict verifier (`java -Xverify:all -Xshare:off`) or a bytecode checker to confirm and locate the malformed method.
  2. Fix or regenerate the bytecode so each branch target has a consistent stack shape.
  3. If verification passes, capture the bci/opcode from the message and report a Graal compiler issue with the reproducing class.
Defensive patterns

Strategy: validation

Validate before calling

// verify stack consistency at every branch target before shipping the class file
// tools: `java -Xverify:all`, ASM CheckClassAdapter, or jacoco/spotbugs bytecode checks

Prevention

When it happens

Trigger: FrameStateBuilder stack check firing for an opcode at a bci where the tracked stack size plus the values pushed by the current operation is smaller than the opcode's pop count — typical of hand-written/modified bytecode that fails verification, or of long/complex values (TWO_SLOT_MARKER handling) confusing the slot accounting.

Common situations: Malformed class files from broken generators or aggressive obfuscators; instrumentation agents inserting pops/branches that leave the stack inconsistent; rare Graal frame-state bugs after unusual store/load sequences — the message includes code, bci, opcode, stack effect and sizes to pinpoint it.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/f88cc8f1b96769ff. Report an issue: GitHub.