oracle/graal · error · RedefinitionException
HierarchyChanged
HierarchyChanged
Error message
class redefinition failed: attempted to change superclass or interfaces
What it means
RedefinitionException(RedefinitionError.HierarchyChanged) thrown under JVMTI restrictions when the replacement class's superclass symbol differs from the original's (newParserKlass.getSuperKlass() vs oldParserKlass.getSuperKlass()). Changing the superclass reshapes the hierarchy; JVMTI redefinition forbids it, while unrestricted mode would proceed to CLASS_HIERARCHY_CHANGED handling.
Source
Thrown at espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java:678
collectedChanges.addRemovedFields(oldFieldsList);
result = ClassChange.SCHEMA_CHANGE;
}
// detect class-level changes
if (newParserKlass.getFlags() != oldParserKlass.getFlags()) {
if (jvmtiRestrictions) {
throw new RedefinitionException(RedefinitionError.ClassModifiersChanged);
}
result = ClassChange.SCHEMA_CHANGE;
}
collectedChanges.addCompatibleFields(compatibleFields);
// detect changes to superclass and implemented interfaces
Klass superKlass = oldKlass.getSuperKlass();
if (!newParserKlass.getSuperKlass().equals(oldParserKlass.getSuperKlass())) {
if (jvmtiRestrictions) {
throw new RedefinitionException(RedefinitionError.HierarchyChanged);
}
result = ClassChange.CLASS_HIERARCHY_CHANGED;
superKlass = getLoadedKlass(newParserKlass.getSuperKlass(), oldKlass);
}
collectedChanges.addSuperKlass((ObjectKlass) superKlass);
ObjectKlass[] newSuperInterfaces = oldKlass.getSuperInterfaces();
if (!Arrays.equals(newParserKlass.getSuperInterfaces(), oldParserKlass.getSuperInterfaces())) {
if (jvmtiRestrictions) {
throw new RedefinitionException(RedefinitionError.HierarchyChanged);
}
result = ClassChange.CLASS_HIERARCHY_CHANGED;
newSuperInterfaces = new ObjectKlass[newParserKlass.getSuperInterfaces().length];
for (int i = 0; i < newParserKlass.getSuperInterfaces().length; i++) {
newSuperInterfaces[i] = (ObjectKlass) getLoadedKlass(newParserKlass.getSuperInterfaces()[i], oldKlass);
}
}
collectedChanges.addSuperInterfaces(newSuperInterfaces);View on GitHub (pinned to a66e9ccd1d)
Solutions
- Keep the same superclass in hotswapped versions; hierarchy edits require a restart.
- Load the modified class as a brand-new class name instead of redefining the existing one.
- Use unrestricted Espresso redefinition when superclass changes must be hotswapped.
Example fix
// before: `class Task` -> hotswap as `class Task extends BaseTask` // after: keep `class Task` for hotswap; introduce hierarchy on restart
Defensive patterns
Strategy: validation
Validate before calling
String oldSuper = superNameOf(oldBytes);
String newSuper = superNameOf(newBytes);
if (!Objects.equals(oldSuper, newSuper)) {
throw new UnsupportedOperationException("superclass changed from " + oldSuper + " to " + newSuper + "; restart required");
} Try / catch
catch (RedefinitionException e) {
if (e.getError() == RedefinitionError.HierarchyChanged) { /* restart context */ }
} Prevention
- Refactor hierarchy changes with a restart, not hotswap.
- Load structurally different classes under new names.
When it happens
Trigger: Restricted hotswap where the new bytes extend a different class (superclass swapped, added, or changed from java.lang.Object to a user class).
Common situations: Introducing a base class into an existing hierarchy during refactoring and hotswapping the child; framework-generated subclasses changing parents between builds; copy-paste development changing extends clauses.
Related errors
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/decd509afb3374de.
Report an issue: GitHub.