chinabugotech/hutool · error · IllegalArgumentException
attribute [{}] cannot mirror for [{}], because [{}] already
Error message
attribute [{}] cannot mirror for [{}], because [{}] already mirrored for [{}] and [{}] already mirrored for [{}] What it means
The other branch of the same check: both mirrored attributes already point at different targets, so neither can additionally mirror the other. The processor treats this as an unrecoverable inconsistent state and throws IllegalArgumentException with both link targets enumerated. The comment notes this case is theoretically unreachable but is guarded defensively.
Source
Thrown at hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java:111
}
// 镜像字段已经跟其他字段形成镜像
else if (!originalAttributeMirrored && mirrorAttributeMirrored) {
errorMsg = CharSequenceUtil.format(
"attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]",
mirror.getAttribute(), original.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked()
);
}
// 两者都形成了镜像,但是都未指向对方,理论上不会存在该情况
else {
errorMsg = CharSequenceUtil.format(
"attribute [{}] cannot mirror for [{}], because [{}] already mirrored for [{}] and [{}] already mirrored for [{}]",
mirror.getAttribute(), original.getAttribute(),
mirror.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked(),
original.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked()
);
}
throw new IllegalArgumentException(errorMsg);
}
/**
* 基本校验
*/
private void checkMirrorRelation(Link annotation, AnnotationAttribute original, AnnotationAttribute mirror) {
// 镜像属性必须存在
checkLinkedAttributeNotNull(original, mirror, annotation);
// 镜像属性返回值必须一致
checkAttributeType(original, mirror);
// 镜像属性上必须存在对应的注解
final Link mirrorAttributeAnnotation = getLinkAnnotation(mirror, RelationType.MIRROR_FOR);
Assert.isTrue(
ObjectUtil.isNotNull(mirrorAttributeAnnotation) && RelationType.MIRROR_FOR.equals(mirrorAttributeAnnotation.type()),
"mirror attribute [{}] of original attribute [{}] must marked by @Link, and also @LinkType.type() must is [{}]",
mirror.getAttribute(), original.getAttribute(), RelationType.MIRROR_FOR
);
checkLinkedSelf(original, mirror);View on GitHub (pinned to 8870454b2a)
Solutions
- Audit custom AnnotationPostProcessor implementations for link mutation; register them after the mirror processor.
- Eliminate duplicate/overlapping @Link MIRROR_FOR declarations across the annotation hierarchy.
- Simplify the annotation composition to a single explicit mirror pair and rebuild.
Defensive patterns
Strategy: try-catch
Try / catch
try { synth.setAttribute(n, v); } catch (UnsupportedOperationException e) { if (e.getMessage().contains("proxied")) { /* mutate underlying */ } else throw e; } Prevention
- Register custom AnnotationPostProcessors after MirrorLinkAnnotationPostProcessor.
- Keep annotation hierarchies free of duplicate MIRROR_FOR links.
When it happens
Trigger: An annotation-processing or synthesis bug where both A and B have already been wrapped as MirroredAnnotationAttribute linked to third parties; can appear when multiple AnnotationPostProcessors run and one has already rewired links before MirrorLinkAnnotationPostProcessor validates.
Common situations: Custom AnnotationPostProcessor registered ahead of MirrorLinkAnnotationPostProcessor that mutates link relationships; duplicate mirror declarations across a meta-annotation chain.
Related errors
- attribute [{}] cannot mirror for [{}], because it's already
- No method for alias: [{}]
- Failed to create AIConfig instance
- No constructor provided
- No constructor provided
AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14).
Data as JSON: /api/errors/d9e4bffd14996ff7.
Report an issue: GitHub.