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

  1. Inspect the class file at the reported bci (javap -c) to identify the offending opcode and its origin.
  2. Remove the instrumentation/agent that injected the non-standard opcode and recompile the class.
  3. 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

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


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