{"record":{"id":"eea72af40fbe2a04","repo":"spring-projects/spring-security","slug":"unused-placeholders-in-template-s-eea72a","errorCode":null,"errorMessage":"Unused placeholders in template: [%s]","messagePattern":"Unused placeholders in template: \\[(.+?)\\]","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"webauthn/src/main/java/org/springframework/security/web/webauthn/registration/HtmlTemplates.java","lineNumber":102,"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":84,"sourceCodeEnd":111,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/HtmlTemplates.java#L84-L111","documentation":"HtmlTemplates' render() validates that all {{placeholder}} tokens in the template were supplied values; after substitution it scans for remaining {{name}} patterns and throws this IllegalStateException listing the leftover placeholder names. It is a developer-time consistency check between the template and the values map.","triggerScenarios":"Calling render(values) with a values map that lacks entries for one or more placeholders present in the template — e.g. a typo in the map key (\"relyingPartyid\" vs \"relyingPartyId\"), or using a template with placeholders not intended for that render call.","commonSituations":"Hand-editing a template and forgetting to pass the new placeholder; renaming a placeholder in the template but not in the caller; reusing a partially populated values map across renders.","solutions":["Compare the reported unused-placeholder names with your values map keys and supply/fix the missing (or misspelled) entries.","If a placeholder should render literally (e.g. example text in docs), pass its value or remove the token from the template.","Note the message is emitted with .formatted() while the check requires [a-zA-Z0-9]+ only — hyphenated or underscored placeholder names will never match the leftover scan, so use the supported name format."],"exampleFix":"// before\nString html = WebAuthnHtmlTemplates.rpRegistration()\n    .render(Map.of(\"relyingPartyId\", rpId)); // missing 'username'\n// after\nString html = WebAuthnHtmlTemplates.rpRegistration()\n    .render(Map.of(\"relyingPartyId\", rpId, \"username\", username));","handlingStrategy":"validation","validationCode":"Set<String> required = new HashSet<>();\nMatcher m = Pattern.compile(\"\\\\{\\\\{([a-zA-Z0-9]+)}}\").matcher(template);\nwhile (m.find()) required.add(m.group(1));\nif (!values.keySet().containsAll(required)) {\n    throw new IllegalStateException(\"Missing values: \" + required.removeAll(values.keySet()));\n}\n","typeGuard":null,"tryCatchPattern":"try {\n    String html = template.render(values);\n} catch (IllegalStateException e) {\n    if (!e.getMessage().startsWith(\"Unused placeholders\")) throw e;\n    log.error(\"Template/values mismatch: {} — check placeholder keys vs map keys\", e.getMessage());\n    throw e;\n}\n","preventionTips":["Extract placeholder names from the template and diff them against your values map keys before render","Rename placeholders in both template and callers atomically","Use only [a-zA-Z0-9] characters in placeholder names — the leftover scan does not detect other formats","Write a unit test per template asserting render succeeds with the canonical example map"],"tags":["webauthn","templates","java"],"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"}