projectlombok/lombok · error · AnnotationValueDecodeFail
Expected a single value, but
Error message
Expected a single value, but ${method.getName()} has an array of values What it means
AnnotationValues.invoke() reflectsively invokes an annotation method and builds the return value. If the method's return type is NOT an array but the annotation source supplied more than one value guess, lombok throws AnnotationValueDecodeFail: the annotation declared a scalar member but was given array syntax values. This guards the contract between the annotation declaration and how it was used in source.
Solutions
- Make the annotation usage match the declaration: pass a single value for a scalar member (value = "x" instead of value = {"x","y"}).
- Or change the annotation member declaration to an array type (e.g. String[] value()) if multiple values are intended.
- If you are an annotation handler author, check isArray/expected.getComponentType() before expecting multiple guesses.
- Check library upgrade notes — annotation member types sometimes change between versions.
Example fix
// before (value() declared as String)
@MyAnn(value = {"a", "b"})
// after
@MyAnn(value = "a") // or declare String[] value() in the annotation Defensive patterns
Strategy: validation
Validate before calling
Class<?> rt = method.getReturnType(); int n = annotationValues.getRawExpressions(method.getName()).size(); if (!rt.isArray() && n > 1) throw new IllegalStateException("Scalar member got " + n + " values"); Type guard
function isScalarUsage(memberType, guessCount) { return memberType !== 'array' && guessCount <= 1; } Try / catch
try { T v = annotationValues.invoke(method); } catch (AnnotationValueDecodeFail e) { log.error("Value/count mismatch for " + method.getName()); } Prevention
- Match annotation usage syntax to the member's declared type (scalar vs array).
- When libraries change member types across versions, update all annotation call sites.
- Unit-test annotation handlers against both single and array forms.
When it happens
Trigger: Calling invoke()/getValue on an AnnotationValues instance where method.getName() has a non-array return type but v.valueGuesses.size() > 1 — i.e. source used value={a,b} (or repeated elements) for a member declared as a scalar (e.g. String value() not String[] value()).
Common situations: Copy-pasting annotation usage where the member was changed from an array type to a scalar (or vice versa) after a library version change; hand-written annotation trees built by AST transformers supplying multiple guesses for a scalar member.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- I can't make sense of this annotation value. Try using a…
- Can't translate to an enum of type
- Can't translate to a class object.
- You must use constant literals in lombok annotations; they…
- Can't translate a to the expected
AI-assisted analysis of projectlombok/lombok@6d6a3e9fec (2026-09-07).
Data as JSON: /api/errors/68c28222d922b9eb.
Report an issue: GitHub.
Appendix: source
Thrown at src/core/lombok/core/AnnotationValues.java:251
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
AnnotationValue v = values.get(method.getName());
if (v == null) {
Object defaultValue = method.getDefaultValue();
if (defaultValue != null) return defaultValue;
throw makeNoDefaultFail(v, method);
}
boolean isArray = false;
Class<?> expected = method.getReturnType();
Object array = null;
if (expected.isArray()) {
isArray = true;
expected = expected.getComponentType();
array = Array.newInstance(expected, v.valueGuesses.size());
}
if (!isArray && v.valueGuesses.size() > 1) {
throw new AnnotationValueDecodeFail(v,
"Expected a single value, but " + method.getName() + " has an array of values", -1);
}
if (v.valueGuesses.size() == 0 && !isArray) {
Object defaultValue = method.getDefaultValue();
if (defaultValue == null) throw makeNoDefaultFail(v, method);
return defaultValue;
}
int idx = 0;
for (Object guess : v.valueGuesses) {
Object result = guess == null ? null : guessToType(guess, expected, v, idx);
if (!isArray) {
if (result == null) {
Object defaultValue = method.getDefaultValue();
if (defaultValue == null) throw makeNoDefaultFail(v, method);
return defaultValue;
}View on GitHub (pinned to 6d6a3e9fec)