quarkusio/quarkus · error
Template extension method declared on must not return void:
Error message
Template extension method declared on must not return void:
What it means
Qute's template extension methods are invoked reflectively/bytecode-generated at build time, so they must return a value usable in an expression. This IllegalStateException is thrown during validation when a method annotated with @TemplateExtension (or in a namespace class) has a void return type, which cannot appear as a value in a template expression.
Source
Thrown at independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ExtensionMethodGenerator.java:85
public static final String MATCH_NAME = "matchName";
public static final String MATCH_NAMES = "matchNames";
public static final String MATCH_REGEX = "matchRegex";
public static final String PRIORITY = "priority";
public static final String NAMESPACE = "namespace";
public static final String PATTERN = "pattern";
public ExtensionMethodGenerator(IndexView index, ClassOutput classOutput) {
super(index, classOutput);
}
public static void validate(MethodInfo method, String namespace) {
if (!Modifier.isStatic(method.flags())) {
throw new IllegalStateException(
"Template extension method declared on " + method.declaringClass().name() + " must be static: " + method);
}
if (method.returnType().kind() == Kind.VOID) {
throw new IllegalStateException("Template extension method declared on " + method.declaringClass().name()
+ " must not return void: " + method);
}
if (Modifier.isPrivate(method.flags())) {
throw new IllegalStateException("Template extension method declared on " + method.declaringClass().name()
+ " must not be private: " + method);
}
}
/**
*
* @param method
* @param matchName
* @param matchNames
* @param matchRegex
* @param priority
* @return the fully qualified name of the generated class
*/
public String generate(MethodInfo method, String matchName, List<String> matchNames, String matchRegex, Integer priority) {View on GitHub (pinned to e1c734241f)
Solutions
- Change the extension method to return a value (the value used in the template expression)
- Remove the @TemplateExtension annotation if the method is not meant to be callable from templates
- Split the logic: keep a void method and add a static wrapper returning the computed result
Example fix
// before
@TemplateExtension
static void format(BigDecimal val) { System.out.println(val); }
// after
@TemplateExtension
static String format(BigDecimal val) { return val.toPlainString(); } Defensive patterns
Strategy: validation
Validate before calling
// Build-time check before registering:
if (java.lang.reflect.Modifier.isStatic(m.getModifiers()) && m.getReturnType() == void.class)
throw new IllegalStateException("@TemplateExtension method must not return void: " + m); Prevention
- Never annotate void methods with @TemplateExtension
- Review extension method signatures in code review
- Add an ArchUnit/custom test asserting extension methods return non-void
When it happens
Trigger: Annotating a static method with @TemplateExtension (or registering it as a namespace extension) where the method's return type is void, e.g. 'void format(String s)'. Detected by ExtensionMethodGenerator.validate during template class generation.
Common situations: Porting utility methods that were mutation-style (returning void) into template extensions; refactoring a method to void after adding the annotation; mistakenly annotating setters or collection mutators.
Related errors
- Invalid global variable name found: %s - supplied by %s -
- Template extension method declared on must be static:
- Template extension method declared on must not be private:
- A template extension method matching multiple names or a reg
- Template extension method declared on must accept at least
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/c182b6e54c4e3795.
Report an issue: GitHub.