{"record":{"id":"bfbd3d0e18f29f0d","repo":"projectlombok/lombok","slug":"delegate-does-not-support-recursion-delegating-t-bfbd3d","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/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java","lineNumber":1022,"sourceCode":"\t\t\n\t\tfor (Binding b : bindings) {\n\t\t\tAnnotationBinding[] anns = null;\n\t\t\tif (b instanceof MethodBinding) anns = ((MethodBinding) b).getAnnotations();\n\t\t\tif (b instanceof FieldBinding) anns = ((FieldBinding) b).getAnnotations();\n\t\t\t// anns = b.getAnnotations() would make a heck of a lot more sense, but that is a late addition to ecj, so would cause NoSuchMethodErrors! Don't use that!\n\t\t\tif (anns == null) continue;\n\t\t\tfor (AnnotationBinding ann : anns) {\n\t\t\t\tchar[][] name = null;\n\t\t\t\ttry {\n\t\t\t\t\tname = ann.getAnnotationType().compoundName;\n\t\t\t\t} catch (Exception ignore) {}\n\t\t\t\t\n\t\t\t\tif (name == null || name.length < 2 || name.length > 3) continue;\n\t\t\t\tif (!Arrays.equals(STRING_LOMBOK, name[0])) continue;\n\t\t\t\tif (!Arrays.equals(STRING_DELEGATE, name[name.length - 1])) continue;\n\t\t\t\tif (name.length == 3 && !Arrays.equals(STRING_EXPERIMENTAL, name[1])) continue;\n\t\t\t\t\n\t\t\t\tthrow new DelegateRecursion(parent.readableName(), b.readableName());\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate static final class BindingTuple {\n\t\tBindingTuple(MethodBinding parameterized, MethodBinding base, char[] fieldName, ASTNode responsible) {\n\t\t\tthis.parameterized = parameterized;\n\t\t\tthis.base = base;\n\t\t\tthis.fieldName = fieldName;\n\t\t\tthis.responsible = responsible;\n\t\t}\n\t\t\n\t\tfinal MethodBinding parameterized, base;\n\t\tfinal char[] fieldName;\n\t\tfinal ASTNode responsible;\n\t\t\n\t\t@Override public String toString() {\n\t\t\treturn String.format(\"{param: %s, base: %s, fieldName: %s}\", parameterized == null ? \"(null)\" : printSig(parameterized), base == null ? \"(null)\" : printSig(base), new String(fieldName));","sourceCodeStart":1004,"sourceCodeEnd":1040,"githubUrl":"https://github.com/projectlombok/lombok/blob/6d6a3e9fec0a9555702561fbeb8eac836575116e/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java#L1004-L1040","documentation":"PatchDelegate refuses to compile code where a type annotated with @Delegate delegates to another type that itself contains @Delegate members. Recursive delegation cannot be resolved, so Eclipse's patched compiler throws DelegateRecursion with the member and type names.","triggerScenarios":"Class A has @Delegate on a field of type B, and B (or a supertype in B's hierarchy being scanned) also has @Delegate members — the recursion guard in canDelegate throws.","commonSituations":"Chained delegation patterns (A delegates to B, B delegates to C), DTO wrappers wrapping other @Delegate wrappers, copying delegation-heavy code between classes.","solutions":["Remove @Delegate from one level of the delegation chain","Replace @Delegate with explicit handwritten forwarding methods on the middle type","Delegate only to the leaf type directly instead of stacking delegates","Upgrade Lombok — newer versions may handle some recursion cases"],"exampleFix":"// before\nclass A { @Delegate B b; }\nclass B { @Delegate C c; }\n// after\nclass A { @Delegate B b; }\nclass B { private C c; C getC() { return c; } } // explicit method instead of @Delegate","handlingStrategy":"validation","validationCode":"function hasRecursiveDelegate(chain) {\n  for (const t of chain) {\n    if (chain.some(o => o !== t && t.delegatesTo.includes(o.name) && o.hasDelegateMembers)) return true;\n  }\n  return false;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never stack @Delegate on types that themselves use @Delegate","Search the codebase for @Delegate usages before adding a new one to a delegated-to type","Use explicit forwarding methods when two levels of delegation are needed"],"tags":["java","lombok","delegate","compile-error"],"backgroundTag":"unsupported-operation","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"}