{"record":{"id":"66ebbb427923249d","repo":"spring-projects/spring-ai","slug":"method-must-have-void-return-type","errorCode":null,"errorMessage":"Method must have void return type: ","messagePattern":"Method must have void return type: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/prompt/SyncMcpPromptListChangedMethodCallback.java","lineNumber":84,"sourceCode":"\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new McpPromptListChangedConsumerMethodException(\n\t\t\t\t\t\"Error invoking prompt 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 prompt 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 SyncMcpPromptListChangedMethodCallback instances.\n\t * <p>\n\t * This builder provides a fluent API for constructing\n\t * SyncMcpPromptListChangedMethodCallback 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/prompt/SyncMcpPromptListChangedMethodCallback.java#L66-L102","documentation":"IllegalArgumentException thrown by SyncMcpPromptListChangedMethodCallback.validateReturnType() when a registered handler method does not return void. The sync callback invokes the handler synchronously and discards any result, so only void methods are accepted; the message includes the method name, declaring class, and offending return type.","triggerScenarios":"Building/registering a SyncMcpPromptListChangedMethodCallback whose method returns boolean, String, List, Mono, CompletableFuture, etc. Validation happens at callback construction, so the error appears at wiring/startup time.","commonSituations":"Reusing an existing query method that returns data as a changed-handler; registering an async-style (Mono-returning) handler against the sync callback; switching from Async to Sync callback without removing return values.","solutions":["Change the handler to return void and perform side effects inside the body.","If you need reactive/async handling, use AsyncMcpPromptListChangedMethodCallback with a Mono<Void> return type instead.","If the method must return data, it belongs to a different callback type (tool/resource), not the list-changed consumer."],"exampleFix":"// before\npublic boolean onPromptsChanged(List<McpSchema.Prompt> prompts) {\n    return reload(prompts);\n}\n// after\npublic void onPromptsChanged(List<McpSchema.Prompt> prompts) {\n    reload(prompts);\n}","handlingStrategy":"validation","validationCode":"if (method.getReturnType() != void.class) {\n    throw new IllegalStateException(\"sync @McpPromptListChanged handlers must return void: \" + method);\n}","typeGuard":"static boolean validSyncReturnType(Method m) {\n    return m.getReturnType() == void.class;\n}","tryCatchPattern":"try {\n    registerSyncHandler(method, bean);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Method must have void return type\")) {\n        throw new ConfigurationException(\"Handler must be void: \" + e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Declare sync changed-handlers as void and use side effects for results.","Use the async callback (Mono<Void>) if you need reactive return types.","Assert handler signatures in unit tests before registration."],"tags":["mcp","return-type","method-signature","sync-callback"],"backgroundTag":"invalid-argument-value","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}