{"record":{"id":"e9dde895179f6421","repo":"oracle/graal","slug":"classmodifierschanged","errorCode":"ClassModifiersChanged","errorMessage":"class redefinition failed: attempted to change the class modifiers","messagePattern":"class redefinition failed: attempted to change the class modifiers","errorType":"error_code","errorClass":"RedefinitionException","httpStatus":null,"severity":"error","filePath":"espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java","lineNumber":667,"sourceCode":"            result = ClassChange.SCHEMA_CHANGE;\n        }\n\n        if (!oldFieldsList.isEmpty()) {\n            if (jvmtiRestrictions) {\n                // only restrict is there's actual removed fields, not only fields with constant\n                // value attribute changes\n                if (oldFieldsList.size() != acceptedChanges.numAcceptedFields) {\n                    throw new RedefinitionException(RedefinitionError.SchemaChanged);\n                }\n            }\n            collectedChanges.addRemovedFields(oldFieldsList);\n            result = ClassChange.SCHEMA_CHANGE;\n        }\n\n        // detect class-level changes\n        if (newParserKlass.getFlags() != oldParserKlass.getFlags()) {\n            if (jvmtiRestrictions) {\n                throw new RedefinitionException(RedefinitionError.ClassModifiersChanged);\n            }\n            result = ClassChange.SCHEMA_CHANGE;\n        }\n\n        collectedChanges.addCompatibleFields(compatibleFields);\n\n        // detect changes to superclass and implemented interfaces\n        Klass superKlass = oldKlass.getSuperKlass();\n        if (!newParserKlass.getSuperKlass().equals(oldParserKlass.getSuperKlass())) {\n            if (jvmtiRestrictions) {\n                throw new RedefinitionException(RedefinitionError.HierarchyChanged);\n            }\n            result = ClassChange.CLASS_HIERARCHY_CHANGED;\n            superKlass = getLoadedKlass(newParserKlass.getSuperKlass(), oldKlass);\n        }\n        collectedChanges.addSuperKlass((ObjectKlass) superKlass);\n\n        ObjectKlass[] newSuperInterfaces = oldKlass.getSuperInterfaces();","sourceCodeStart":649,"sourceCodeEnd":685,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java#L649-L685","documentation":"RedefinitionException(RedefinitionError.ClassModifiersChanged) thrown under JVMTI restrictions when the class-level access/modifier flags differ between oldParserKlass and newParserKlass (e.g. public, final, abstract, interface bit changes). Without restrictions the change would just be classified as SCHEMA_CHANGE; with jvmtiRestrictions it is rejected outright.","triggerScenarios":"Restricted hotswap where the replacement class file has different class-level flags: adding/removing final or abstract, changing visibility, or altering the interface bit.","commonSituations":"Making a class final (or un-finaling it to allow mocking/subclassing) and hotswapping; toggling abstract during a refactor; annotation-processing or AOP tools rewriting class flags.","solutions":["Emit replacement bytes with the identical class-level modifiers; restrict hotswap edits to method bodies and constant values.","Apply modifier changes at restart, where the class is loaded anew.","If live modifier changes are required, use Espresso's unrestricted redefinition mode."],"exampleFix":"// before: `public class Foo` -> hotswap with `public final class Foo`\n// after: keep `public class Foo` for the hotswap; add `final` on restart","handlingStrategy":"validation","validationCode":"// compare class access flags (bytes offset 0..1 after constant pool via a parser)\nint oldFlags = classFlagsOf(oldBytes);\nint newFlags = classFlagsOf(newBytes);\nif (oldFlags != newFlags) {\n    throw new UnsupportedOperationException(\"class modifiers changed, restart required\");\n}","typeGuard":null,"tryCatchPattern":"catch (RedefinitionException e) {\n    if (e.getError() == RedefinitionError.ClassModifiersChanged) { /* restart or revert flags */ }\n}","preventionTips":["Do not toggle final/abstract/visibility during hotswap sessions.","Beware bytecode processors that silently rewrite flags (e.g. mock/AOP agents)."],"tags":["espresso","hotswap","class-redefinition","jvmti","modifiers"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}