{"record":{"id":"324ecc68e64bd1ca","repo":"spring-projects/spring-security","slug":"unused-placeholders-in-template-s","errorCode":null,"errorMessage":"Unused placeholders in template: [%s]","messagePattern":"Unused placeholders in template: \\[(.+?)\\]","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"web/src/main/java/org/springframework/security/web/server/ui/HtmlTemplates.java","lineNumber":100,"sourceCode":"\t\t * Render the template. All placeholders MUST have a corresponding value. If a\n\t\t * placeholder does not have a corresponding value, throws\n\t\t * {@link IllegalStateException}.\n\t\t * @return the rendered template\n\t\t */\n\t\tString render() {\n\t\t\tString template = this.template;\n\t\t\tfor (String key : this.values.keySet()) {\n\t\t\t\tString pattern = \"{{\" + key + \"}}\";\n\t\t\t\ttemplate = template.replace(pattern, this.values.get(key));\n\t\t\t}\n\n\t\t\tString unusedPlaceholders = Pattern.compile(\"\\\\{\\\\{([a-zA-Z0-9]+)}}\")\n\t\t\t\t.matcher(template)\n\t\t\t\t.results()\n\t\t\t\t.map((result) -> result.group(1))\n\t\t\t\t.collect(Collectors.joining(\", \"));\n\t\t\tif (StringUtils.hasLength(unusedPlaceholders)) {\n\t\t\t\tthrow new IllegalStateException(\"Unused placeholders in template: [%s]\".formatted(unusedPlaceholders));\n\t\t\t}\n\n\t\t\treturn template;\n\t\t}\n\n\t}\n\n}\n","sourceCodeStart":82,"sourceCodeEnd":109,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/web/src/main/java/org/springframework/security/web/server/ui/HtmlTemplates.java#L82-L109","documentation":"HtmlTemplates.render validates that every {{placeholder}} in the template string is actually supplied by the caller's context. If any placeholder remains unreplaced after rendering (because no value was passed for it), rendering aborts with IllegalStateException listing the unused placeholders. This catches template/context mismatches early instead of shipping a page with literal {{...}} text.","triggerScenarios":"Calling the public render(template, model/context) method where the template contains a {{name}} placeholder that has no corresponding key in the provided values map — e.g. a typo in the key name or a template updated without updating the caller.","commonSituations":"Editing an HTML template to add a new placeholder but forgetting to pass its value; passing context keys with wrong casing; reusing one template across code paths where one path omits a value.","solutions":["Add the missing key/value to the context map passed to render","Remove the {{placeholder}} from the template if it is no longer needed","Fix key-name typos/casing so the placeholder matches a supplied key","Ensure every code path rendering the template supplies all placeholders"],"exampleFix":"// before\nHtmlTemplates.render(\"<h1>{{title}}</h1>{{subtitle}}\", Map.of(\"title\", \"Hi\"))\n// after\nHtmlTemplates.render(\"<h1>{{title}}</h1>{{subtitle}}\", Map.of(\"title\", \"Hi\", \"subtitle\", \"Welcome\"))","handlingStrategy":"validation","validationCode":"// verify placeholders are covered before render\nSet<String> placeholders = extractPlaceholders(template); // regex \\{\\{([a-zA-Z0-9]+)}}\nif (!context.keySet().containsAll(placeholders)) {\n    throw new IllegalArgumentException(\"Missing values: \" + placeholders.removeAll(context.keySet()));\n}","typeGuard":null,"tryCatchPattern":"try {\n    return HtmlTemplates.render(template, context);\n} catch (IllegalStateException ex) {\n    logger.error(\"Template mismatch: \" + ex.getMessage());\n    throw ex;\n}","preventionTips":["Add a unit test per template asserting every placeholder has a supplied value","Never edit templates without updating all render call sites","Use constants for context keys instead of inline strings"],"tags":["spring-security","template-rendering","illegal-state"],"backgroundTag":"missing-required-argument","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}