{"record":{"id":"af6ac804731850da","repo":"junit-team/junit5","slug":"detected-cycle-in-inner-class-hierarchy-between-s","errorCode":null,"errorMessage":"Detected cycle in inner class hierarchy between %s and %s","messagePattern":"Detected cycle in inner class hierarchy between (.+?) and (.+?)","errorType":"exception","errorClass":"JUnitException","httpStatus":null,"severity":"error","filePath":"junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java","lineNumber":2022,"sourceCode":"\t * exception}; otherwise, this method returns the supplied {@code Throwable}.\n\t */\n\tstatic Throwable getUnderlyingCause(Throwable t) {\n\t\tif (t instanceof InvocationTargetException ite) {\n\t\t\treturn getUnderlyingCause(ite.getTargetException());\n\t\t}\n\t\treturn t;\n\t}\n\n\t/**\n\t * @since 1.13.2\n\t */\n\t@API(status = INTERNAL, since = \"1.13.2\")\n\tpublic enum CycleErrorHandling {\n\n\t\tTHROW_EXCEPTION {\n\t\t\t@Override\n\t\t\tvoid handle(Class<?> clazz, Class<?> enclosing) {\n\t\t\t\tthrow new JUnitException(\"Detected cycle in inner class hierarchy between %s and %s\".formatted(\n\t\t\t\t\tclazz.getName(), enclosing.getName()));\n\t\t\t}\n\t\t},\n\n\t\tABORT_VISIT {\n\t\t\t@Override\n\t\t\tvoid handle(Class<?> clazz, Class<?> enclosing) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t};\n\n\t\tabstract void handle(Class<?> clazz, Class<?> enclosing);\n\t}\n\n}\n","sourceCodeStart":2004,"sourceCodeEnd":2038,"githubUrl":"https://github.com/junit-team/junit5/blob/f070c699a08b5d8393df9afd147af5c5e90bb21b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java#L2004-L2038","documentation":"JUnitException thrown by ReflectionUtils.detectInnerClassCycle via CycleErrorHandling.THROW_EXCEPTION when an inner class's superclass also appears in its enclosing-class chain - i.e. the inner-class hierarchy contains a cycle. Used during nested-test and @Nested discovery to prevent infinite recursion when walking enclosing classes.","triggerScenarios":"A class A.B (inner of A) whose superclass is A itself, or any inner class whose enclosing chain loops back through its superclass. Encountered by findNestedClasses / isNestedClassPresent when called with CycleErrorHandling.THROW_EXCEPTION (the default for several discovery predicates).","commonSituations":"Bytecode-engineered classes (ASM, ByteBuddy, Mockito mocks) that emit inner-class metadata creating a cycle; obfuscated classes; rarely hand-written pathological hierarchies; generated test fixtures.","solutions":["Refactor the offending class so its enclosing chain does not include its superclass","Use ReflectionUtils.isNestedClassPresent(..., CycleErrorHandling.ABORT_VISIT) to skip rather than throw","Regenerate the bytecode without the cycle if a tool produced it","File a bug against the bytecode tool if cycles appear without user action"],"exampleFix":"// before\nclass Outer {\n  class Inner extends Outer {} // cycle: Inner's superclass is its enclosing class\n}\n\n// after - option A: break the cycle\nclass Outer {\n  static class Inner {}\n}\n\n// after - option B: tolerate the cycle at scan time\nReflectionUtils.isNestedClassPresent(clazz, predicate, CycleErrorHandling.ABORT_VISIT);","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    ReflectionUtils.isNestedClassPresent(clazz, predicate, CycleErrorHandling.THROW_EXCEPTION);\n} catch (JUnitException e) {\n    // fall back to skipping the cycle rather than aborting discovery\n    ReflectionUtils.isNestedClassPresent(clazz, predicate, CycleErrorHandling.ABORT_VISIT);\n}","preventionTips":["Avoid inner classes that extend their own enclosing class","Use CycleErrorHandling.ABORT_VISIT when scanning untrusted or generated class hierarchies","Validate generated bytecode for inner-class metadata cycles"],"tags":["reflection","inner-class","cycle-detection","discovery"],"backgroundTag":null,"analyzedSha":"f070c699a08b5d8393df9afd147af5c5e90bb21b","analyzedAt":"2026-08-11T20:31:00.530Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}