spring-projects/spring-security · error · IllegalArgumentException

suffix cannot be empty

Error message

suffix cannot be empty

What it means

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.

Source

Thrown at crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java:186

	 * Creates a new instance.
	 * @param idForEncode the id used to lookup which {@link PasswordEncoder} should be
	 * used for {@link #encode(CharSequence)}
	 * @param idToPasswordEncoder a Map of id to {@link PasswordEncoder} used to determine
	 * which {@link PasswordEncoder} should be used for
	 * @param idPrefix the prefix that denotes the start of the id in the encoded results
	 * @param idSuffix the suffix that denotes the end of an id in the encoded results
	 * {@link #matches(CharSequence, String)}
	 */
	public DelegatingPasswordEncoder(String idForEncode, Map<String, PasswordEncoder> idToPasswordEncoder,
			String idPrefix, String idSuffix) {
		if (idForEncode == null) {
			throw new IllegalArgumentException("idForEncode cannot be null");
		}
		if (idPrefix == null) {
			throw new IllegalArgumentException("prefix cannot be null");
		}
		if (idSuffix == null || idSuffix.isEmpty()) {
			throw new IllegalArgumentException("suffix cannot be empty");
		}
		if (idPrefix.contains(idSuffix)) {
			throw new IllegalArgumentException("idPrefix " + idPrefix + " cannot contain idSuffix " + idSuffix);
		}

		if (!idToPasswordEncoder.containsKey(idForEncode)) {
			throw new IllegalArgumentException(
					"idForEncode " + idForEncode + "is not found in idToPasswordEncoder " + idToPasswordEncoder);
		}
		for (String id : idToPasswordEncoder.keySet()) {
			if (id == null) {
				continue;
			}
			if (!idPrefix.isEmpty() && id.contains(idPrefix)) {
				throw new IllegalArgumentException("id " + id + " cannot contain " + idPrefix);
			}
			if (id.contains(idSuffix)) {
				throw new IllegalArgumentException("id " + id + " cannot contain " + idSuffix);

View on GitHub (pinned to 96852e8860)

Solutions

  1. Pass the conventional suffix "}" as the idSuffix argument (matching the {id}encoded format)
  2. Ensure the variable bound to idSuffix is not null or empty before constructing; add a check in your config code
  3. Use PasswordEncoderFactories.createDelegatingPasswordEncoder() instead of constructing DelegatingPasswordEncoder manually

Example fix

// before
PasswordEncoder encoder = new DelegatingPasswordEncoder("bcrypt", encoders, "{", "");
// after
PasswordEncoder encoder = new DelegatingPasswordEncoder("bcrypt", encoders, "{", "}");
Defensive patterns

Strategy: validation

Validate before calling

if (idSuffix == null || idSuffix.isEmpty()) {
    throw new IllegalArgumentException("idSuffix must be non-empty, e.g. \"}\"");
}
new DelegatingPasswordEncoder(idForEncode, encoders, idPrefix, idSuffix);

Try / catch

try {
    return new DelegatingPasswordEncoder(idForEncode, encoders, "{", "}");
} catch (IllegalArgumentException e) {
    log.error("DelegatingPasswordEncoder construction failed: {}", e.getMessage());
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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().

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/a0e6c65cff0a4e52. Report an issue: GitHub.