{"record":{"id":"55a98ed7d987e365","repo":"spring-projects/spring-ai","slug":"uri-variable-parameters-must-be-of-type-string","errorCode":null,"errorMessage":"URI variable parameters must be of type String: ${method} in ${declaringClass}, parameter of type ${paramType} is not valid","messagePattern":"URI variable parameters must be of type String: (.+?) in (.+?), parameter of type (.+?) is not valid","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/AbstractMcpResourceMethodCallback.java","lineNumber":383,"sourceCode":"\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Method must have parameters for all URI variables. Expected \" + this.uriVariables.size()\n\t\t\t\t\t\t\t+ \" URI variable parameters, but found \" + uriVarParamCount + \": \" + method.getName()\n\t\t\t\t\t\t\t+ \" in \" + method.getDeclaringClass().getName() + \". URI variables: \" + this.uriVariables);\n\t\t}\n\n\t\t// Check that all non-special parameters are String type (for URI variables)\n\t\tfor (Parameter param : parameters) {\n\t\t\t// Skip @McpProgressToken annotated parameters\n\t\t\tif (param.isAnnotationPresent(McpProgressToken.class)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tClass<?> paramType = param.getType();\n\t\t\tif (!McpSyncRequestContext.class.isAssignableFrom(paramType)\n\t\t\t\t\t&& !McpAsyncRequestContext.class.isAssignableFrom(paramType) && !isExchangeOrContextType(paramType)\n\t\t\t\t\t&& !ReadResourceRequest.class.isAssignableFrom(paramType)\n\t\t\t\t\t&& !McpMeta.class.isAssignableFrom(paramType) && !String.class.isAssignableFrom(paramType)) {\n\t\t\t\tthrow new IllegalArgumentException(\"URI variable parameters must be of type String: \" + method.getName()\n\t\t\t\t\t\t+ \" in \" + method.getDeclaringClass().getName() + \", parameter of type \" + paramType.getName()\n\t\t\t\t\t\t+ \" is not valid\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected abstract Object assignExchangeType(Class<?> paramType, Object exchange);\n\n\t/**\n\t * Builds the arguments array for invoking the method.\n\t * <p>\n\t * This method constructs an array of arguments based on the method's parameter types\n\t * and the available values (exchange, request, URI variables, progress token).\n\t * @param method The method to build arguments for\n\t * @param exchange The server exchange\n\t * @param request The resource request\n\t * @param uriVariableValues Map of URI variable names to their values\n\t * @return An array of arguments for the method invocation","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/AbstractMcpResourceMethodCallback.java#L365-L401","documentation":"Thrown when a non-special parameter of an @McpResource method with URI variables is not assignable to any of the allowed types (McpSyncRequestContext, McpAsyncRequestContext, exchange/context types, ReadResourceRequest, McpMeta, String). URI variable parameters must be String.","triggerScenarios":"Declaring a URI variable parameter as Integer, UUID, or a custom type (e.g. read(String table, int id)) with a variable URI; validateParametersWithUriVariables iterates parameters and throws for any type outside the allowed set.","commonSituations":"Using Integer/UUID for an id path variable expecting automatic conversion; leaving a DTO parameter in the signature; changing a String param to a typed one during refactoring.","solutions":["Change the parameter type to String and parse/convert manually inside the method","Ensure the parameter is annotated with @McpProgressToken if it is meant to be a progress token, so it is skipped","Remove the parameter if it is not needed (remember URI variable count must match)"],"exampleFix":"// before\n@McpResource(uri = \"docs://{id}\")\npublic String read(UUID id) { ... }\n// after\n@McpResource(uri = \"docs://{id}\")\npublic String read(String id) { return read(UUID.fromString(id)); }","handlingStrategy":"validation","validationCode":"for (Parameter p : m.getParameters()) {\n    Class<?> t = p.getType();\n    if (!String.class.isAssignableFrom(t) && !isSpecialParam(t) && !p.isAnnotationPresent(McpProgressToken.class))\n        throw new IllegalStateException(\"URI variable parameter \" + p + \" must be String\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always declare URI variable parameters as String and parse manually","Do not use UUID/Integer/DTO types in resource method signatures","Annotate progress-token params with @McpProgressToken so they are skipped"],"tags":["mcp","java","validation","type-mismatch","uri-template"],"backgroundTag":"type-mismatch","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"}