oracle/graal · info · PermanentBailoutException

concurrent setting of breakpoint

Error message

concurrent setting of breakpoint

What it means

Thrown by BytecodeParser when it encounters the BREAKPOINT opcode (0xc2) in the bytecode stream while parsing. The breakpoint opcode is written into bytecode by debuggers (JVMTI SetBreakpoint) at runtime; a class file parsed with that opcode present means the bytecode was mutated concurrently, so Graal permanently bails out rather than compile transient debug state.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/BytecodeParser.java:5975

            case INVOKESPECIAL  : cpi = stream.readCPI(); genInvokeSpecial(cpi, opcode); break;
            case INVOKESTATIC   : cpi = stream.readCPI(); genInvokeStatic(cpi, opcode); break;
            case INVOKEINTERFACE: cpi = stream.readCPI(); genInvokeInterface(cpi, opcode); break;
            case INVOKEDYNAMIC  : cpi = stream.readCPI4(); genInvokeDynamic(cpi, opcode); break;
            case NEW            : genNewInstance(stream.readCPI()); break;
            case NEWARRAY       : genNewPrimitiveArray(stream.readLocalIndex()); break;
            case ANEWARRAY      : genNewObjectArray(stream.readCPI()); break;
            case ARRAYLENGTH    : genArrayLength(); break;
            case ATHROW         : genThrow(); break;
            case CHECKCAST      : genCheckCast(stream.readCPI()); break;
            case INSTANCEOF     : genInstanceOf(stream.readCPI()); break;
            case MONITORENTER   : genMonitorEnter(frameState.pop(JavaKind.Object), stream.nextBCI()); break;
            case MONITOREXIT    : genMonitorExit(frameState.pop(JavaKind.Object), null, stream.nextBCI(), false, true); break;
            case MULTIANEWARRAY : genNewMultiArray(stream.readCPI()); break;
            case IFNULL         : genIfNull(Condition.EQ); break;
            case IFNONNULL      : genIfNull(Condition.NE); break;
            case GOTO_W         : genGoto(); break;
            case JSR_W          : genJsr(stream.readBranchDest()); break;
            case BREAKPOINT     : throw new PermanentBailoutException("concurrent setting of breakpoint");
            default             : throw new PermanentBailoutException("Unsupported opcode %d (%s) [bci=%d]", opcode, nameOf(opcode), bci);
        }
        // @formatter:on
        // Checkstyle: resume
    }

    private void genArrayLength() {
        ValueNode array = frameState.pop(JavaKind.Object);
        frameState.push(JavaKind.Int, append(genArrayLength(array)));
    }

    @Override
    public ResolvedJavaMethod getMethod() {
        return method;
    }

    @Override
    public Bytecode getCode() {

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Remove the breakpoint in the debugger and let HotSpot re-attempt compilation of the method.
  2. Recompile/relaunch without the debugger attached for benchmarking or production JIT behavior.
  3. If caused by an agent, configure it to use non-breakpoint instrumentation (e.g. class retransformation instead).
Defensive patterns

Strategy: fallback

Try / catch

// transient debugger-induced bailout; HotSpot retries after the breakpoint is removed.

Prevention

When it happens

Trigger: stream bytecode == Bytecodes.BREAKPOINT during processBytecode — i.e. a breakpoint was set in the method between the moment HotSpot fetched the bytecode and Graal parsing it (concurrent JVMTI SetBreakpoint), or a class file literally containing opcode 0xc2.

Common situations: Attaching a debugger (or a coverage/profiling agent using breakpoints) to a JVM while Graal compiles the same method; race between debugger instrumentation and JIT compilation; class files emitted by bytecode tools that misuse 0xc2.

Related errors


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