spring-projects/spring-security · error · IllegalArgumentException

id cannot contain

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.

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.

Appendix: 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)