spring-projects/spring-security · error · IllegalArgumentException

idPrefix {idPrefix} cannot contain idSuffix {idSuffix}

Error message

idPrefix {idPrefix} cannot contain idSuffix {idSuffix}

What it means

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.

Source

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

	 * @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);
			}
		}
		this.idForEncode = idForEncode;

View on GitHub (pinned to 96852e8860)

Solutions

  1. Choose non-overlapping delimiters, keeping the default idPrefix="{" and idSuffix="}"
  2. Log the two values before construction and verify neither is a substring of the other
  3. Revert to PasswordEncoderFactories.createDelegatingPasswordEncoder() if custom delimiters are not actually needed

Example fix

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

Strategy: validation

Validate before calling

if (idPrefix != null && idSuffix != null && idPrefix.contains(idSuffix)) {
    throw new IllegalStateException("idPrefix must not contain idSuffix");
}

Try / catch

try {
    return new DelegatingPasswordEncoder(idForEncode, encoders, idPrefix, idSuffix);
} catch (IllegalArgumentException e) {
    throw new ConfigurationException("Bad prefix/suffix delimiters: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Invoking the DelegatingPasswordEncoder constructor where idPrefix.contains(idSuffix) is true, e.g. prefix "{," suffix "," or prefix "{" and suffix "{{".

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

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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