{"record":{"id":"720f531e6206cb4b","repo":"spring-projects/spring-security","slug":"prefix-cannot-be-null","errorCode":null,"errorMessage":"prefix cannot be null","messagePattern":"prefix cannot be null","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java","lineNumber":183,"sourceCode":"\t}\n\n\t/**\n\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);","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java#L165-L201","documentation":"DelegatingPasswordEncoder's full constructor requires a non-null idPrefix (default \"{\") and throws IllegalArgumentException('prefix cannot be null') when it is null. The prefix/suffix delimit the encoder id inside encoded password strings and are needed to parse {id}... on matches().","triggerScenarios":"Calling new DelegatingPasswordEncoder(idForEncode, idToPasswordEncoder, null, \"}\") or passing a prefix variable that resolved to null; customizing delimiters and forgetting the prefix while setting the suffix.","commonSituations":"Externalizing prefix/suffix to properties where the prefix key is absent; intentionally wanting no prefix by passing null instead of an empty-safe value; copy-paste of the suffix argument into the prefix slot.","solutions":["Pass non-null delimiters, typically \"{\" and \"}\".","To use no delimiters, pass empty strings where allowed, but note the suffix must be non-null and non-empty and the prefix must not contain the suffix.","Validate configured delimiter values before constructing the encoder."],"exampleFix":"// before\nString prefix = props.get(\"pw.prefix\"); // null\nPasswordEncoder encoder = new DelegatingPasswordEncoder(\"bcrypt\", encoders, prefix, \"}\");\n// after\nString prefix = props.getOrDefault(\"pw.prefix\", \"{\");\nPasswordEncoder encoder = new DelegatingPasswordEncoder(\"bcrypt\", encoders, prefix, \"}\");","handlingStrategy":"validation","validationCode":"if (idPrefix == null) {\n    throw new IllegalArgumentException(\"idPrefix must be non-null (default \\\"{\\\")\");\n}\nif (idSuffix == null || idSuffix.isEmpty()) {\n    throw new IllegalArgumentException(\"idSuffix must be non-empty (default \\\"}\\\")\");\n}\nPasswordEncoder encoder = new DelegatingPasswordEncoder(idForEncode, idToPasswordEncoder, idPrefix, idSuffix);","typeGuard":"boolean areValidDelimiters(String prefix, String suffix) {\n    return prefix != null && suffix != null && !suffix.isEmpty() && !prefix.contains(suffix);\n}","tryCatchPattern":"try {\n    encoder = new DelegatingPasswordEncoder(idForEncode, idToPasswordEncoder, idPrefix, idSuffix);\n} catch (IllegalArgumentException ex) {\n    encoder = new DelegatingPasswordEncoder(idForEncode, idToPasswordEncoder, \"{\", \"}\");\n}","preventionTips":["Keep the standard delimiters { and } unless you control the entire stored-password format","Default prefix/suffix from properties instead of letting missing keys inject null","Remember related constraints: non-empty suffix and prefix must not contain suffix — validate all at once","Test matches() on an existing stored password after changing delimiters to avoid lockouts"],"tags":["password","encoding","null-argument","constructor"],"backgroundTag":"null-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"}