{"record":{"id":"a0e6c65cff0a4e52","repo":"spring-projects/spring-security","slug":"suffix-cannot-be-empty","errorCode":null,"errorMessage":"suffix cannot be empty","messagePattern":"suffix cannot be empty","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java","lineNumber":186,"sourceCode":"\t * Creates a new instance.\n\t * @param idForEncode the id used to lookup which {@link PasswordEncoder} should be\n\t * used for {@link #encode(CharSequence)}\n\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);","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java#L168-L204","documentation":"The DelegatingPasswordEncoder constructor validates that the idSuffix (the delimiter closing the encoder id inside an encoded password, e.g. '}') is non-null and non-empty. An empty suffix would make it impossible to delimit the encoder id from the password hash, so the library refuses construction with this IllegalArgumentException.","triggerScenarios":"Calling the DelegatingPasswordEncoder constructor (typically via PasswordEncoderFactories or directly with new DelegatingPasswordEncoder(idForEncode, idToPasswordEncoder, idPrefix, idSuffix)) passing null or \"\" (empty string) as the idSuffix argument.","commonSituations":"Custom password-encoder wiring in Spring Security configuration where the '{id}encoded' prefix format is being customized; copying the constructor call and accidentally dropping or emptying the suffix parameter; programmatic bean definition instead of PasswordEncoderFactories.createDelegatingPasswordEncoder().","solutions":["Pass the conventional suffix \"}\" as the idSuffix argument (matching the {id}encoded format)","Ensure the variable bound to idSuffix is not null or empty before constructing; add a check in your config code","Use PasswordEncoderFactories.createDelegatingPasswordEncoder() instead of constructing DelegatingPasswordEncoder manually"],"exampleFix":"// before\nPasswordEncoder encoder = new DelegatingPasswordEncoder(\"bcrypt\", encoders, \"{\", \"\");\n// after\nPasswordEncoder encoder = new DelegatingPasswordEncoder(\"bcrypt\", encoders, \"{\", \"}\");","handlingStrategy":"validation","validationCode":"if (idSuffix == null || idSuffix.isEmpty()) {\n    throw new IllegalArgumentException(\"idSuffix must be non-empty, e.g. \\\"}\\\"\");\n}\nnew DelegatingPasswordEncoder(idForEncode, encoders, idPrefix, idSuffix);","typeGuard":null,"tryCatchPattern":"try {\n    return new DelegatingPasswordEncoder(idForEncode, encoders, \"{\", \"}\");\n} catch (IllegalArgumentException e) {\n    log.error(\"DelegatingPasswordEncoder construction failed: {}\", e.getMessage());\n    throw e;\n}","preventionTips":["Always pass \"}\" as the suffix for the standard {id}password format","Prefer PasswordEncoderFactories.createDelegatingPasswordEncoder() over manual construction","Validate constructor arguments in your @Bean method before returning the encoder"],"tags":["spring-security","illegal-argument","password-encoding","constructor-validation"],"backgroundTag":"invalid-constructor-argument","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"}