projectlombok/lombok · error · DelegateRecursion
@Delegate does not support recursion (delegating to a type…
Error message
@Delegate does not support recursion (delegating to a type that itself has @Delegate members). Member "%s" is @Delegate in type "%s"
What it means
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.
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
Example fix
// before
class A { @Delegate B b; }
class B { @Delegate C c; }
// after
class A { @Delegate(types = {C.class}) B b; }
class B { @Delegate C c; } Defensive patterns
Strategy: validation
Validate before calling
boolean hasNestedDelegate(Class<?> t) {
try {
for (Field f : t.getDeclaredFields())
if (f.isAnnotationPresent(lombok.Delegate.class) || f.isAnnotationPresent(lombok.experimental.Delegate.class)) return true;
} catch (Throwable ignored) {}
return false;
}
// assert !hasNestedDelegate(TargetType.class) before annotating with @Delegate Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- I can't make sense of this annotation value. Try using a…
- Can't translate to an enum of type
- @Delegate does not support recursion (delegating to a type…
- Expected a single value, but
- Can't translate to a class object.
AI-assisted analysis of projectlombok/lombok@6d6a3e9fec (2026-09-07).
Data as JSON: /api/errors/3d0a8e15427e910f.
Report an issue: GitHub.
Appendix: source
Thrown at src/core/lombok/javac/handlers/HandleDelegate.java:377
public DelegateRecursion(String type, String member) {
this.type = type;
this.member = member;
}
}
public void addMethodBindings(List<MethodSig> signatures, ClassType ct, JavacTypes types, Set<String> banList) throws DelegateRecursion {
TypeSymbol tsym = ct.asElement();
if (tsym == null) return;
for (Symbol member : tsym.getEnclosedElements()) {
for (Compound am : member.getAnnotationMirrors()) {
String name = null;
try {
name = am.type.tsym.flatName().toString();
} catch (Exception ignore) {}
if ("lombok.Delegate".equals(name) || "lombok.experimental.Delegate".equals(name)) {
throw new DelegateRecursion(ct.tsym.name.toString(), member.name.toString());
}
}
if (member.getKind() != ElementKind.METHOD) continue;
if (member.isStatic()) continue;
if (member.isConstructor()) continue;
ExecutableElement exElem = (ExecutableElement)member;
if (!exElem.getModifiers().contains(Modifier.PUBLIC)) continue;
ExecutableType methodType = (ExecutableType) types.asMemberOf(ct, member);
String sig = printSig(methodType, member.name, types);
if (!banList.add(sig)) continue; //If add returns false, it was already in there
boolean isDeprecated = (member.flags() & DEPRECATED) != 0;
signatures.add(new MethodSig(member.name, methodType, isDeprecated, exElem));
}
for (Type type : types.directSupertypes(ct)) {
if (type instanceof ClassType) {
addMethodBindings(signatures, (ClassType) type, types, banList);
}View on GitHub (pinned to 6d6a3e9fec)