{"record":{"id":"6c7d80388243b209","repo":"spring-projects/spring-ai","slug":"method-must-have-void-return-type-method-getn","errorCode":null,"errorMessage":"Method must have void return type: \" + method.getName() + \" in \" + method.getDeclaringClass().getName() + \" returns \" + returnType.getName()","messagePattern":"Method must have void return type: \" \\+ method\\.getName\\(\\) \\+ \" in \" \\+ method\\.getDeclaringClass\\(\\)\\.getName\\(\\) \\+ \" returns \" \\+ returnType\\.getName\\(\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/resource/SyncMcpResourceListChangedMethodCallback.java","lineNumber":84,"sourceCode":"\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new McpResourceListChangedConsumerMethodException(\n\t\t\t\t\t\"Error invoking resource list changed consumer method: \" + this.method.getName(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Validates that the method return type is compatible with the resource list changed\n\t * consumer callback.\n\t * @param method The method to validate\n\t * @throws IllegalArgumentException if the return type is not compatible\n\t */\n\t@Override\n\tprotected void validateReturnType(Method method) {\n\t\tClass<?> returnType = method.getReturnType();\n\n\t\tif (returnType != void.class) {\n\t\t\tthrow new IllegalArgumentException(\"Method must have void return type: \" + method.getName() + \" in \"\n\t\t\t\t\t+ method.getDeclaringClass().getName() + \" returns \" + returnType.getName());\n\t\t}\n\t}\n\n\t/**\n\t * Create a new builder.\n\t * @return A new builder instance\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Builder for creating SyncMcpResourceListChangedMethodCallback instances.\n\t * <p>\n\t * This builder provides a fluent API for constructing\n\t * SyncMcpResourceListChangedMethodCallback instances with the required parameters.\n\t */","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/resource/SyncMcpResourceListChangedMethodCallback.java#L66-L102","documentation":"Sync resource-list-changed callback methods must return void. This IllegalArgumentException is thrown at registration by validateReturnType when the method returns any other type. Sync callbacks cannot report results, so non-void returns are rejected.","triggerScenarios":"Registering a sync (SyncMcpResourceListChangedMethodCallback) callback whose return type is boolean, String, Mono, or any other non-void type.","commonSituations":"Sharing one handler method between sync and async registrations (async needs Mono<Void>), or returning a status value the developer expects the framework to log.","solutions":["Change the method's return type to void for the sync callback.","If reactive handling is needed, register the method with the async callback (Mono<Void> allowed) instead.","Move side-effect results into internal fields/logging rather than the return value."],"exampleFix":"// before\npublic boolean onResourcesChanged(List<McpSchema.Resource> r) { ...; return true; }\n// after\npublic void onResourcesChanged(List<McpSchema.Resource> r) { ... }","handlingStrategy":"validation","validationCode":"if (m.getReturnType() != void.class) throw new IllegalStateException(\"sync resource-list-changed callback must return void\");","typeGuard":"static boolean hasValidSyncReturnType(Method m) {\n    return m.getReturnType() == void.class;\n}","tryCatchPattern":"try {\n    registry.register(syncCallback);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Method must have void return type\")) {\n        throw new IllegalStateException(\"Sync callback must be declared void\", e);\n    }\n    throw e;\n}","preventionTips":["Declare sync handlers as void; use the async callback when you need Mono<Void>.","Avoid sharing one method between sync and async registrations unless it returns void and you only need sync semantics."],"tags":["java","mcp","return-type","validation"],"backgroundTag":"type-mismatch","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}