spring-projects/spring-security · error · IllegalArgumentException

id {id} cannot contain {idSuffix}

Error message

id {id} cannot contain {idSuffix}

What it means

For the same reason as the prefix check, an encoder id containing the idSuffix (e.g. '}') would break extraction of the id from '{id}encodedPassword'. The constructor throws this IllegalArgumentException when any map key contains the suffix.

Source

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

			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;
		this.passwordEncoderForEncode = idToPasswordEncoder.get(idForEncode);
		this.idToPasswordEncoder = new HashMap<>(idToPasswordEncoder);
		this.idPrefix = idPrefix;
		this.idSuffix = idSuffix;
	}

	/**
	 * Sets the {@link PasswordEncoder} to delegate to for
	 * {@link #matches(CharSequence, String)} if the id is not mapped to a
	 * {@link PasswordEncoder}.
	 *
	 * <p>
	 * The encodedPassword provided will be the full password passed in including the
	 * {"id"} portion.* For example, if the password of "{notmapped}foobar" was used, the
	 * "id" would be "notmapped" and the encodedPassword passed into the

View on GitHub (pinned to 96852e8860)

Solutions

  1. Sanitize encoder ids to exclude the idSuffix (and idPrefix) characters before registering them
  2. Use plain alphanumeric ids for encoders
  3. Validate with id.contains(idSuffix) yourself in configuration code before constructing the delegating encoder

Example fix

// before
encoders.put("bcrypt}", new BCryptPasswordEncoder());
// after
encoders.put("bcrypt", new BCryptPasswordEncoder());
Defensive patterns

Strategy: validation

Validate before calling

for (String id : encoders.keySet()) {
    if (id != null && id.contains("}")) {
        throw new IllegalStateException("Encoder id must not contain '}': " + id);
    }
}

Try / catch

try {
    return new DelegatingPasswordEncoder(idForEncode, encoders, "{", "}");
} catch (IllegalArgumentException e) {
    log.error("Encoder id conflicts with prefix/suffix: {}", e.getMessage());
    throw e;
}

Prevention

When it happens

Trigger: Registering an encoder id in idToPasswordEncoder whose key contains the idSuffix string, e.g. key "bc}rypt" with idSuffix "}".

Common situations: Dynamically generated ids that embed delimiter characters; pasting encoded-password fragments as ids; config-provided encoder names containing '}'.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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