{"record":{"id":"de52e83ab3354456","repo":"spring-projects/spring-security","slug":"idprefix-idprefix-cannot-contain-idsuffix-idsuf","errorCode":null,"errorMessage":"idPrefix {idPrefix} cannot contain idSuffix {idSuffix}","messagePattern":"idPrefix (.+?) cannot contain idSuffix (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java","lineNumber":189,"sourceCode":"\t * @param idToPasswordEncoder a Map of id to {@link PasswordEncoder} used to determine\n\t * which {@link PasswordEncoder} should be used for\n\t * @param idPrefix the prefix that denotes the start of the id in the encoded results\n\t * @param idSuffix the suffix that denotes the end of an id in the encoded results\n\t * {@link #matches(CharSequence, String)}\n\t */\n\tpublic DelegatingPasswordEncoder(String idForEncode, Map<String, PasswordEncoder> idToPasswordEncoder,\n\t\t\tString idPrefix, String idSuffix) {\n\t\tif (idForEncode == null) {\n\t\t\tthrow new IllegalArgumentException(\"idForEncode cannot be null\");\n\t\t}\n\t\tif (idPrefix == null) {\n\t\t\tthrow new IllegalArgumentException(\"prefix cannot be null\");\n\t\t}\n\t\tif (idSuffix == null || idSuffix.isEmpty()) {\n\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;","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java#L171-L207","documentation":"The idPrefix (e.g. '{') and idSuffix (e.g. '}') delimit the encoder id in encoded passwords. If the prefix already contains the suffix as a substring, extracting the id would be ambiguous, so the constructor rejects the combination with this IllegalArgumentException.","triggerScenarios":"Invoking the DelegatingPasswordEncoder constructor where idPrefix.contains(idSuffix) is true, e.g. prefix \"{,\" suffix \",\" or prefix \"{\" and suffix \"{{\".","commonSituations":"Customizing the {id}password wrapping format with unusual delimiters and accidentally choosing overlapping strings; typos where both prefix and suffix are set to the same value.","solutions":["Choose non-overlapping delimiters, keeping the default idPrefix=\"{\" and idSuffix=\"}\"","Log the two values before construction and verify neither is a substring of the other","Revert to PasswordEncoderFactories.createDelegatingPasswordEncoder() if custom delimiters are not actually needed"],"exampleFix":"// before\nnew DelegatingPasswordEncoder(\"bcrypt\", encoders, \"{\", \"{\");\n// after\nnew DelegatingPasswordEncoder(\"bcrypt\", encoders, \"{\", \"}\");","handlingStrategy":"validation","validationCode":"if (idPrefix != null && idSuffix != null && idPrefix.contains(idSuffix)) {\n    throw new IllegalStateException(\"idPrefix must not contain idSuffix\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return new DelegatingPasswordEncoder(idForEncode, encoders, idPrefix, idSuffix);\n} catch (IllegalArgumentException e) {\n    throw new ConfigurationException(\"Bad prefix/suffix delimiters: \" + e.getMessage(), e);\n}","preventionTips":["Stick to the default '{' and '}' delimiters","Assert prefix and suffix are distinct, non-overlapping strings in config validation"],"tags":["spring-security","illegal-argument","password-encoding","delimiter-conflict"],"backgroundTag":"invalid-argument-value","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"}