{"id":"a63147d9a998af8a","repo":"spring-projects/spring-framework","slug":"placeholder-match-is-not-valid","errorCode":null,"errorMessage":"Placeholder [{match}] is not valid","messagePattern":"Placeholder \\[(.+?)\\] is not valid","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/interceptor/CustomizableTraceInterceptor.java","lineNumber":397,"sourceCode":"\t\tString[] argumentTypeShortNames = new String[argumentTypes.length];\n\t\tfor (int i = 0; i < argumentTypeShortNames.length; i++) {\n\t\t\targumentTypeShortNames[i] = ClassUtils.getShortName(argumentTypes[i]);\n\t\t}\n\t\tmatcher.appendReplacement(output,\n\t\t\t\tMatcher.quoteReplacement(StringUtils.arrayToCommaDelimitedString(argumentTypeShortNames)));\n\t}\n\n\t/**\n\t * Checks to see if the supplied {@code String} has any placeholders\n\t * that are not specified as constants on this class and throws an\n\t * {@code IllegalArgumentException} if so.\n\t */\n\tprivate static void checkForInvalidPlaceholders(String message) throws IllegalArgumentException {\n\t\tMatcher matcher = PATTERN.matcher(message);\n\t\twhile (matcher.find()) {\n\t\t\tString match = matcher.group();\n\t\t\tif (!ALLOWED_PLACEHOLDERS.contains(match)) {\n\t\t\t\tthrow new IllegalArgumentException(\"Placeholder [\" + match + \"] is not valid\");\n\t\t\t}\n\t\t}\n\t}\n\n}\n","sourceCodeStart":379,"sourceCodeEnd":403,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/e8729d043887bf0d0baf91e062e909b56eb2b708/spring-aop/src/main/java/org/springframework/aop/interceptor/CustomizableTraceInterceptor.java#L379-L403","documentation":"checkForInvalidPlaceholders throws IllegalArgumentException at setter time when setEnterMessage/setExitMessage/setExceptionMessage contains a $[...] token that is not one of the eight allowed placeholders. The valid set is fixed (methodName, targetClassName, targetClassShortName, returnValue, argumentTypes, arguments, exception, invocationTime); any other match of the regex $\\[\\p{Alpha}+] is rejected.","triggerScenarios":"Calling interceptor.setEnterMessage(\"Entering $[method] for $[target]\") where $[method] and $[target] are not recognised placeholders (the real names are $[methodName] and $[targetClassName]).","commonSituations":"Misspelling a placeholder; copying an example from an outdated tutorial; using a $[var] shell-style variable that happens to match the regex.","solutions":["Use only the documented placeholder constants (e.g. $[methodName], $[targetClassName], $[returnValue]).","Respect per-message restrictions: enterMessage cannot use $[returnValue], $[exception], or $[invocationTime].","Reference CustomizableTraceInterceptor.PLACEHOLDER_* constants instead of hardcoding strings."],"exampleFix":"// before\ninterceptor.setEnterMessage(\"Entering $[method] on $[target]\");\n\n// after\ninterceptor.setEnterMessage(\n    \"Entering \" + CustomizableTraceInterceptor.PLACEHOLDER_METHOD_NAME +\n    \" on \" + CustomizableTraceInterceptor.PLACEHOLDER_TARGET_CLASS_NAME);","handlingStrategy":"validation","validationCode":"static final Set<String> ALLOWED = CustomizableTraceInterceptor.ALLOWED_PLACEHOLDERS;\nvoid validate(String msg) {\n    Matcher m = Pattern.compile(\"\\\\$\\\\[\\\\p{Alpha}+]\").matcher(msg);\n    while (m.find()) {\n        if (!ALLOWED.contains(m.group())) {\n            throw new IllegalArgumentException(\"bad placeholder: \" + m.group());\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Reference PLACEHOLDER_* constants rather than typing placeholder strings.","Externalise trace templates into a config file covered by a startup test.","Review the per-message placeholder restrictions in the Javadoc."],"tags":["spring-aop","tracing","configuration","validation"],"analyzedSha":"e8729d043887bf0d0baf91e062e909b56eb2b708","analyzedAt":"2026-08-04T19:07:39.725Z","schemaVersion":2}