oracle/graal · error · RedefinitionException

InvalidClassFormat

InvalidClassFormat

Error message

{}

What it means

Thrown as RedefinitionException with RedefinitionError.InvalidClassFormat when Espresso's class redefinition (hotswap) pipeline fails to parse the new class bytes: ParserKlassProvider raised a ValidationException or ParserException.ClassFormatError. The '{}' message is the underlying parser message describing the malformed structure. Redefinition aborts before any class changes are applied.

Solutions

  1. Inspect the exception message (formatted into the RedefinitionException) to identify the exact parse/verification problem.
  2. Regenerate the class bytes with a known-good compiler or verified bytecode tool (javac, ASM with COMPUTE_FRAMES) and retry the redefinition.
  3. Validate the bytes before submitting: parse them with a standalone class-file parser (e.g. ASM ClassReader) to confirm they are well formed.
  4. If a patcher/instrumentation agent produced the bytes, disable it or fix its transformer to isolate the source of corruption.

Example fix

// before
byte[] bytes = Files.readAllBytes(Paths.get("NewVersion.class")); // possibly corrupt
redefinition.redefine(klass, bytes);
// after
byte[] bytes = Files.readAllBytes(Paths.get("NewVersion.class"));
try { new org.objectweb.asm.ClassReader(bytes); } // fail fast on malformed bytes
catch (RuntimeException e) { throw new IllegalArgumentException("bad class file", e); }
redefinition.redefine(klass, bytes);
Defensive patterns

Strategy: validation

Validate before calling

// sanity-parse replacement bytes before submitting them for redefinition
try {
    new org.objectweb.asm.ClassReader(bytes).accept(null, 0);
} catch (RuntimeException e) {
    throw new IllegalArgumentException("malformed class file: " + e.getMessage(), e);
}

Try / catch

catch (RedefinitionException e) {
    if (e.getError() == RedefinitionError.InvalidClassFormat) {
        // report, keep old class version running, regenerate bytes
    }
}

Prevention

When it happens

Trigger: Calling the redefinition/hotswap API with byte[] that is not a structurally valid class file (truncated bytes, corrupted constant pool, bad magic/major version layout, instrumented bytecode from a broken agent); redefining a patched class whose patched bytes are invalid.

Common situations: Hotswap agents (JRebel-style tools, debuggers, custom instrumentation) producing corrupt or partially-written class files; reading class bytes from a stream that was closed early; a bytecode transformer bug injecting invalid attributes; file-transfer corruption of .class files.

Related errors


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

Appendix: source

Thrown at espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java:235

            ParserKlass parserKlass;
            ParserKlass newParserKlass = null;
            ClassChange classChange;
            DetectedChange detectedChange = new DetectedChange();
            StaticObject loader = klass.getDefiningClassLoader();
            TypeSymbols typeSymbols = klass.getContext().getTypes();
            try {
                parserKlass = ParserKlassProvider.parseKlassWithHostErrors(ClassRegistry.ClassDefinitionInfo.EMPTY, context.getClassLoadingEnv(), loader,
                                typeSymbols.fromClassNameEntry(hotSwapInfo.getName()), bytes);
                if (hotSwapInfo.isPatched()) {
                    byte[] patched = hotSwapInfo.getPatchedBytes();
                    newParserKlass = parserKlass;
                    // we detect changes against the patched bytecode
                    parserKlass = ParserKlassProvider.parseKlassWithHostErrors(ClassRegistry.ClassDefinitionInfo.EMPTY, context.getClassLoadingEnv(), loader,
                                    typeSymbols.fromClassNameEntry(hotSwapInfo.getNewName()),
                                    patched);
                }
            } catch (ValidationException | ParserException.ClassFormatError validationOrBadFormat) {
                throw new RedefinitionException(RedefinitionError.InvalidClassFormat, validationOrBadFormat.getMessage());
            } catch (ParserException.UnsupportedClassVersionError unsupportedClassVersionError) {
                throw new RedefinitionException(RedefinitionError.UnsupportedVersion, unsupportedClassVersionError.getMessage());
            } catch (ParserException.NoClassDefFoundError noClassDefFoundError) {
                // see HotSpot VM_RedefineClasses::load_new_class_versions
                throw new RedefinitionException(RedefinitionError.NamesDontMatch, noClassDefFoundError.getMessage());
            } catch (ParserException parserException) {
                throw EspressoError.shouldNotReachHere("Not a validation nor parser exception", parserException);
            }
            classChange = detectClassChanges(parserKlass, klass, detectedChange, newParserKlass, jvmtiRestrictions);
            if (classChange == ClassChange.CLASS_HIERARCHY_CHANGED && detectedChange.getSuperKlass() != null) {
                // keep track of unhandled changed super classes
                ObjectKlass superKlass = detectedChange.getSuperKlass();
                ObjectKlass oldSuperKlass = klass.getSuperKlass();
                ObjectKlass commonSuperKlass = (ObjectKlass) oldSuperKlass.findLeastCommonAncestor(superKlass);
                while (superKlass != commonSuperKlass) {
                    superClassChanges.add(superKlass);
                    superKlass = superKlass.getSuperKlass();
                }

View on GitHub (pinned to a66e9ccd1d)