spring-projects/spring-security · error · IllegalArgumentException

id {id} cannot contain {idPrefix}

Error message

id {id} cannot contain {idPrefix}

What it means

Each encoder id in the map becomes part of the stored password string ({id}...). If an id itself contains the idPrefix, the stored encoding would be ambiguous and unparseable, so the constructor rejects it with this IllegalArgumentException.

Source

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

			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;
		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>

View on GitHub (pinned to 96852e8860)

Solutions

  1. Use bare encoder ids that contain only characters outside the prefix/suffix (letters, digits, hyphen)
  2. Strip any '{' or '}' from map keys before registering encoders
  3. If you have a stored '{bcrypt}' style string, unwrap it: key = value.substring(1, value.length()-1)

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 in idToPasswordEncoder whose key contains the idPrefix (e.g. key "my{encoder" while idPrefix is "{").

Common situations: Composing encoder ids dynamically from user or config input that accidentally embeds '{'; building id strings like prefix + name; copy-paste of full '{id}' strings used as map keys instead of the bare id.

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/43c2f6397188eee5. Report an issue: GitHub.