{"record":{"id":"57f47c6275ff9f98","repo":"spring-projects/spring-security","slug":"id-id-cannot-contain-idsuffix","errorCode":null,"errorMessage":"id {id} cannot contain {idSuffix}","messagePattern":"id (.+?) cannot contain (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java","lineNumber":204,"sourceCode":"\t\t\tthrow new IllegalArgumentException(\"suffix cannot be empty\");\n\t\t}\n\t\tif (idPrefix.contains(idSuffix)) {\n\t\t\tthrow new IllegalArgumentException(\"idPrefix \" + idPrefix + \" cannot contain idSuffix \" + idSuffix);\n\t\t}\n\n\t\tif (!idToPasswordEncoder.containsKey(idForEncode)) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"idForEncode \" + idForEncode + \"is not found in idToPasswordEncoder \" + idToPasswordEncoder);\n\t\t}\n\t\tfor (String id : idToPasswordEncoder.keySet()) {\n\t\t\tif (id == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!idPrefix.isEmpty() && id.contains(idPrefix)) {\n\t\t\t\tthrow new IllegalArgumentException(\"id \" + id + \" cannot contain \" + idPrefix);\n\t\t\t}\n\t\t\tif (id.contains(idSuffix)) {\n\t\t\t\tthrow new IllegalArgumentException(\"id \" + id + \" cannot contain \" + idSuffix);\n\t\t\t}\n\t\t}\n\t\tthis.idForEncode = idForEncode;\n\t\tthis.passwordEncoderForEncode = idToPasswordEncoder.get(idForEncode);\n\t\tthis.idToPasswordEncoder = new HashMap<>(idToPasswordEncoder);\n\t\tthis.idPrefix = idPrefix;\n\t\tthis.idSuffix = idSuffix;\n\t}\n\n\t/**\n\t * Sets the {@link PasswordEncoder} to delegate to for\n\t * {@link #matches(CharSequence, String)} if the id is not mapped to a\n\t * {@link PasswordEncoder}.\n\t *\n\t * <p>\n\t * The encodedPassword provided will be the full password passed in including the\n\t * {\"id\"} portion.* For example, if the password of \"{notmapped}foobar\" was used, the\n\t * \"id\" would be \"notmapped\" and the encodedPassword passed into the","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java#L186-L222","documentation":"For the same reason as the prefix check, an encoder id containing the idSuffix (e.g. '}') would break extraction of the id from '{id}encodedPassword'. The constructor throws this IllegalArgumentException when any map key contains the suffix.","triggerScenarios":"Registering an encoder id in idToPasswordEncoder whose key contains the idSuffix string, e.g. key \"bc}rypt\" with idSuffix \"}\".","commonSituations":"Dynamically generated ids that embed delimiter characters; pasting encoded-password fragments as ids; config-provided encoder names containing '}'.","solutions":["Sanitize encoder ids to exclude the idSuffix (and idPrefix) characters before registering them","Use plain alphanumeric ids for encoders","Validate with id.contains(idSuffix) yourself in configuration code before constructing the delegating encoder"],"exampleFix":"// before\nencoders.put(\"bcrypt}\", new BCryptPasswordEncoder());\n// after\nencoders.put(\"bcrypt\", new BCryptPasswordEncoder());","handlingStrategy":"validation","validationCode":"for (String id : encoders.keySet()) {\n    if (id != null && id.contains(\"}\")) {\n        throw new IllegalStateException(\"Encoder id must not contain '}': \" + id);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    return new DelegatingPasswordEncoder(idForEncode, encoders, \"{\", \"}\");\n} catch (IllegalArgumentException e) {\n    log.error(\"Encoder id conflicts with prefix/suffix: {}\", e.getMessage());\n    throw e;\n}","preventionTips":["Sanitize encoder ids with id.replace(\"}\", \"\") or reject them at config load time","Use only letters, digits and hyphens in encoder ids"],"tags":["spring-security","illegal-argument","password-encoding","identifier-validation"],"backgroundTag":"invalid-identifier-format","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}