{"record":{"id":"3d0a8e15427e910f","repo":"projectlombok/lombok","slug":"delegate-does-not-support-recursion-delegating-t","errorCode":null,"errorMessage":"@Delegate does not support recursion (delegating to a type that itself has @Delegate members). Member \"%s\" is @Delegate in type \"%s\"","messagePattern":"@Delegate does not support recursion \\(delegating to a type that itself has @Delegate members\\)\\. Member \"(.+?)\" is @Delegate in type \"(.+?)\"","errorType":"exception","errorClass":"DelegateRecursion","httpStatus":null,"severity":"error","filePath":"src/core/lombok/javac/handlers/HandleDelegate.java","lineNumber":377,"sourceCode":"\t\tpublic DelegateRecursion(String type, String member) {\n\t\t\tthis.type = type;\n\t\t\tthis.member = member;\n\t\t}\n\t}\n\t\n\tpublic void addMethodBindings(List<MethodSig> signatures, ClassType ct, JavacTypes types, Set<String> banList) throws DelegateRecursion {\n\t\tTypeSymbol tsym = ct.asElement();\n\t\tif (tsym == null) return;\n\t\t\n\t\tfor (Symbol member : tsym.getEnclosedElements()) {\n\t\t\tfor (Compound am : member.getAnnotationMirrors()) {\n\t\t\t\tString name = null;\n\t\t\t\ttry {\n\t\t\t\t\tname = am.type.tsym.flatName().toString();\n\t\t\t\t} catch (Exception ignore) {}\n\t\t\t\t\n\t\t\t\tif (\"lombok.Delegate\".equals(name) || \"lombok.experimental.Delegate\".equals(name)) {\n\t\t\t\t\tthrow new DelegateRecursion(ct.tsym.name.toString(), member.name.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (member.getKind() != ElementKind.METHOD) continue;\n\t\t\tif (member.isStatic()) continue;\n\t\t\tif (member.isConstructor()) continue;\n\t\t\tExecutableElement exElem = (ExecutableElement)member;\n\t\t\tif (!exElem.getModifiers().contains(Modifier.PUBLIC)) continue;\n\t\t\tExecutableType methodType = (ExecutableType) types.asMemberOf(ct, member);\n\t\t\tString sig = printSig(methodType, member.name, types);\n\t\t\tif (!banList.add(sig)) continue; //If add returns false, it was already in there\n\t\t\tboolean isDeprecated = (member.flags() & DEPRECATED) != 0;\n\t\t\tsignatures.add(new MethodSig(member.name, methodType, isDeprecated, exElem));\n\t\t}\n\n\t\tfor (Type type : types.directSupertypes(ct)) {\n\t\t\tif (type instanceof ClassType) {\n\t\t\t\taddMethodBindings(signatures, (ClassType) type, types, banList);\n\t\t\t}","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/projectlombok/lombok/blob/6d6a3e9fec0a9555702561fbeb8eac836575116e/src/core/lombok/javac/handlers/HandleDelegate.java#L359-L395","documentation":"HandleDelegate throws DelegateRecursion (an error that lombok reports to the user) when a type delegated to via @Delegate itself contains members annotated with @Delegate. Lombok resolves delegation recursively, so nested @Delegate would loop infinitely; it cuts this off explicitly.","triggerScenarios":"Annotating a field/method with @Delegate whose type (or any type in its hierarchy, including via interfaces) also has fields or methods annotated with @Delegate — detected in addMethodBindings when scanning the target type's members for a lombok.Delegate / lombok.experimental.Delegate annotation.","commonSituations":"Composing delegation across multiple classes, e.g. class A delegates to B while B delegates to C; often arises after a refactor added @Delegate deeper in the graph, or with generated code (IDE-added @Delegate) that the developer did not notice.","solutions":["Remove @Delegate from the target type (B) and instead list its methods via @Delegate(types=...) on A, or delegate directly to the innermost type","Break the cycle by re-structuring: delegate only to leaf types that have no @Delegate members","Expose the needed methods manually on the delegating class instead of relying on chained delegation"],"exampleFix":"// before\nclass A { @Delegate B b; }\nclass B { @Delegate C c; }\n// after\nclass A { @Delegate(types = {C.class}) B b; }\nclass B { @Delegate C c; }","handlingStrategy":"validation","validationCode":"boolean hasNestedDelegate(Class<?> t) {\n    try {\n        for (Field f : t.getDeclaredFields())\n            if (f.isAnnotationPresent(lombok.Delegate.class) || f.isAnnotationPresent(lombok.experimental.Delegate.class)) return true;\n    } catch (Throwable ignored) {}\n    return false;\n}\n// assert !hasNestedDelegate(TargetType.class) before annotating with @Delegate","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only delegate to leaf types with no @Delegate members of their own","Use @Delegate(types=...) to flatten multi-level delegation","Search the codebase for @Delegate before adding another layer"],"tags":["lombok","delegate","recursion","compile-time"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6d6a3e9fec0a9555702561fbeb8eac836575116e","analyzedAt":"2026-09-07T23:18:01.841Z","contentChangedAt":"2026-09-07T23:18:01.841Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}