chinabugotech/hutool · error · UnsupportedOperationException
proxied annotation can not reset attributes
Error message
proxied annotation can not reset attributes
What it means
SynthesizedAnnotationProxy is an immutable view over a synthesized annotation; calling setAttribute on it is explicitly blocked because the proxy has no backing store to mutate. The lambda mapped to setAttribute always throws UnsupportedOperationException regardless of arguments. All other attribute access (getAttributeValue, hasAttribute, etc.) is supported.
Solutions
- Mutate the underlying SynthesizedAnnotation (annotation.setAttribute) before proxying, not the proxy itself.
- If you need a modified annotation, build a new synthesized annotation with the changed value rather than calling setAttribute on the proxy.
- Type-check: if the object is a SynthesizedAnnotationProxy, skip setAttribute calls.
Example fix
// before
Object proxy = synthesizer.synthesize(annotation);
((SynthesizedAnnotation) proxy).setAttribute("k", v); // throws
// after
SynthesizedAnnotation synth = synthesizer.getSynthesizedAnnotation(...);
synth.setAttribute("k", v);
Object proxy = synthesizer.synthesize(annotation); Defensive patterns
Strategy: validation
Validate before calling
if (proxy instanceof SynthesizedAnnotationProxy) throw new IllegalStateException("cannot setAttribute on immutable proxy"); Type guard
static boolean isMutableSynthesis(Object o) { return o instanceof SynthesizedAnnotation && !(o instanceof SynthesizedAnnotationProxy); } Try / catch
try { ((SynthesizedAnnotation)obj).setAttribute(k,v); } catch (UnsupportedOperationException e) { /* rebuild from underlying */ } Prevention
- Mutate the underlying SynthesizedAnnotation before proxying.
- Do not treat a synthesized proxy as a mutable annotation.
When it happens
Trigger: Obtaining a proxy via AnnotationSynthesizer.synthesize(...) (or any API returning a SynthesizedAnnotationProxied) and invoking setAttribute(name, value) on it. This typically happens when generic annotation-mutation code treats the synthesized result like a mutable AnnotationSynopsis.
Common situations: Frameworks that try to override annotation attributes at runtime for testing/config and receive a synthesized proxy instead of the raw synthesized annotation; misreading the API and assuming the proxy supports the full SynthesizedAnnotation mutation surface.
Related errors
- attribute [ ] cannot mirror for [ ], because [ ] already…
- attribute [ ] cannot mirror for [ ], because it's already…
- Cache values Iterator is not support to modify.
- Comparator ordering cannot be changed after the first…
- No constructor provided
AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14).
Data as JSON: /api/errors/592abd8547e8ffb3.
Report an issue: GitHub.
Appendix: source
Thrown at hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java:114
return Opt.ofNullable(methods.get(method.getName()))
.map(m -> m.apply(method, args))
.orElseGet(() -> ReflectUtil.invoke(annotation.getAnnotation(), method, args));
}
// ========================= 代理方法 =========================
void loadMethods() {
// 非用户属性
methods.put("toString", (method, args) -> proxyToString());
methods.put("hashCode", (method, args) -> proxyHashCode());
methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSynthesizedAnnotation());
methods.put("getRoot", (method, args) -> annotation.getRoot());
methods.put("getVerticalDistance", (method, args) -> annotation.getVerticalDistance());
methods.put("getHorizontalDistance", (method, args) -> annotation.getHorizontalDistance());
methods.put("hasAttribute", (method, args) -> annotation.hasAttribute((String) args[0], (Class<?>) args[1]));
methods.put("getAttributes", (method, args) -> annotation.getAttributes());
methods.put("setAttribute", (method, args) -> {
throw new UnsupportedOperationException("proxied annotation can not reset attributes");
});
methods.put("getAttributeValue", (method, args) -> annotation.getAttributeValue((String) args[0]));
methods.put("annotationType", (method, args) -> annotation.annotationType());
// 可以被合成的用户属性
Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType()))
.filter(m -> !methods.containsKey(m.getName()))
.forEach(m -> methods.put(m.getName(), (method, args) -> proxyAttributeValue(method)));
}
private String proxyToString() {
final String attributes = Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType()))
.filter(AnnotationUtil::isAttributeMethod)
.map(method -> CharSequenceUtil.format(
"{}={}", method.getName(), proxyAttributeValue(method))
)
.collect(Collectors.joining(", "));
return CharSequenceUtil.format("@{}({})", annotation.annotationType().getName(), attributes);View on GitHub (pinned to 8870454b2a)