quarkusio/quarkus · error · MessageBundleException
A message bundle method must return java.lang.String: %s#%s
Error message
A message bundle method must return java.lang.String: %s#%s
What it means
Message bundle interface methods are generated to return localized text, so every method must declare java.lang.String as its return type. If a method returns anything else (int, StringBuilder, custom type, void), MessageBundleProcessor throws during build for that method.
Source
Thrown at extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java:1038
cc.extends_(ClassDesc.of(defaultBundleImpl));
}
cc.defaultConstructor();
// key -> method
Map<String, MessageMethod> keyMap = new LinkedHashMap<>();
List<MethodInfo> methods = new ArrayList<>(bundleInterfaceWrapper.methods());
// Sort methods
methods.sort(Comparator.comparing(MethodInfo::name).thenComparing(Comparator.comparing(MethodInfo::toString)));
for (MethodInfo method : methods) {
cc.method(methodDescOf(method), mc -> {
List<ParamVar> params = new ArrayList<>(method.parametersCount());
for (int i = 0; i < method.parametersCount(); i++) {
String paramName = method.parameterName(i);
params.add(mc.parameter(paramName != null ? paramName : "arg" + i, i));
}
if (!method.returnType().name().equals(DotNames.STRING)) {
throw new MessageBundleException(
String.format("A message bundle method must return java.lang.String: %s#%s",
bundleInterface, method.name()));
}
LOG.debugf("Found message bundle method %s on %s", method, bundleInterface);
AnnotationInstance messageAnnotation;
if (defaultBundleInterface != null) {
MethodInfo defaultBundleMethod = bundleInterfaceWrapper.method(method.name(),
method.parameterTypes().toArray(new Type[] {}));
if (defaultBundleMethod == null) {
throw new MessageBundleException(
String.format("Default bundle method not found on %s: %s", bundleInterface, method));
}
messageAnnotation = defaultBundleMethod.annotation(Names.MESSAGE);
} else {
messageAnnotation = method.annotation(Names.MESSAGE);
}
View on GitHub (pinned to e1c734241f)
Solutions
- Change the method's return type to String: `String hello(String name);`
- If structured data is needed, return String of the rendered message and parse in the caller, or move non-message logic out of the bundle interface.
- Rebuild to confirm all bundle methods now return String.
Example fix
// before
@Message("Hello {name}")
StringBuilder hello(String name);
// after
@Message("Hello {name}")
String hello(String name); Defensive patterns
Strategy: validation
Validate before calling
// Every bundle method must return String
for (java.lang.reflect.Method m : AppMessages.class.getMethods()) {
if (m.getDeclaringClass() != AppMessages.class) continue;
if (m.getReturnType() != String.class) {
throw new IllegalStateException("Must return String: " + m);
}
} Prevention
- Always declare String return types in bundle interfaces
- Never copy service-class signatures into bundles
- Enable IDE inspections for annotation-validated interfaces
When it happens
Trigger: Declaring `StringBuilder hello(String name);` or `int count();` on a @MessageBundle interface; accidental generic/wrapper return types like `Object` or `Optional<String>`.
Common situations: Copying method signatures from a service class into the bundle interface; auto-completing the wrong overload; refactoring the return type for one purpose and forgetting templates need plain Strings.
Related errors
- Message bundle name [%s] declared on %s must be a valid name
- Message bundle interface name conflict - [%s] is used for bo
- Locale of [%s] conflicts with the locale [%s] of the default
- Cannot register [%s] - a localized message bundle interface
- @MessageBundle must be declared on an interface: {bundleClas
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/9368218db064f1b2.
Report an issue: GitHub.