oracle/graal · error · PermanentBailoutException
Unsupported opcode %d (%s) [bci=%d]
Error message
Unsupported opcode %d (%s) [bci=%d]
What it means
BytecodeParser's default dispatch case: the opcode being parsed is not one of the handled JVM bytecodes. Graal's frontend implements the JVM instruction set exhaustively; reaching the default branch means the stream contains an opcode the parser genuinely does not support (not a breakpoint — that is a separate case), so it permanently bails out with the opcode number, mnemonic, and bci.
Source
Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/BytecodeParser.java:5976
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() {
return code;View on GitHub (pinned to a66e9ccd1d)
Solutions
- Inspect the class file at the reported bci (javap -c) to identify the offending opcode and its origin.
- Remove the instrumentation/agent that injected the non-standard opcode and recompile the class.
- Regenerate the bytecode with a standard compiler so only defined JVM instructions appear.
Defensive patterns
Strategy: validation
Validate before calling
// scan the method's bytecode for non-standard opcodes before deployment // (javap -c or a ClassReader walk rejecting opcodes not in the JVM spec)
Prevention
- Validate generated/obfuscated class files with a strict verifier in CI.
- Never emit implementation-specific opcodes (0xfe/0xff) in shipped bytecode.
When it happens
Trigger: processBytecode hitting an opcode with no case in the switch — e.g. IMPDEP1/IMPDEP2 (0xfe/0xff, historically used by debuggers/JVMTI), or any value the parser has no handler for, at the reported bci.
Common situations: Debugger/agent-instrumented bytecode using implementation-specific opcodes; malformed class files with garbage opcodes; experimental or non-standard bytecode emitted by a custom language compiler targeting the JVM.
Related errors
- Can not duplicate block with JSR data
- Too many loops in method
- Irreducible
- Non-reducible loop requires too much duplication. Setting %s
- Block that is reached by a fall through end of code is reach
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/4c414eb4c6d7d63d.
Report an issue: GitHub.