spring-projects/spring-security · error · IllegalArgumentException

Given that there is no default password encoder configured…

Error message

Given that there is no default password encoder configured, each password must have a password encoding prefix. Please either prefix this password with '{noop}' or set a default password encoder in `DelegatingPasswordEncoder`.

What it means

When matching, if the stored password contains neither the idPrefix nor the idSuffix, it carries no encoder id at all. With no default encoder configured for such raw-format passwords, DelegatingPasswordEncoder cannot pick a matcher and throws this IllegalArgumentException, suggesting '{noop}' prefixing or a default encoder.

Solutions

  1. Re-prefix stored passwords with their encoding id, e.g. '{noop}secret' or '{bcrypt}$2a$10$...'
  2. Call setDefaultPasswordEncoderForMatches(encoder) to handle unprefixed passwords (e.g. with a delegating NoOp or BCrypt default)
  3. Re-encode the stored credentials into the {id}... format via a data migration
  4. For dev/test data, insert passwords using the DelegatingPasswordEncoder's own encode() output

Example fix

// before
// stored: $2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
// after
// stored: {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
Defensive patterns

Strategy: validation

Validate before calling

if (storedPassword != null && !storedPassword.startsWith("{")) {
    throw new IllegalStateException("Stored password lacks {id} prefix: " + storedPassword);
}

Try / catch

try {
    return encoder.matches(rawPassword, storedPassword);
} catch (IllegalArgumentException e) {
    // fall back to legacy matching for unprefixed hashes
    return legacyEncoder.matches(rawPassword, storedPassword);
}

Prevention

When it happens

Trigger: matches() with a password that is plain text or an unprefixed hash (e.g. old '$2a$10$...' BCrypt string without '{bcrypt}') on a DelegatingPasswordEncoder created without setDefaultPasswordEncoderForMatches.

Common situations: Migrating a legacy user store to Spring Security's delegating encoder; hand-inserted test users with raw '{noop}pass' missing; seeding data directly into the DB without the {id} wrapper.

Related errors


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

Appendix: source

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

	 */
	private class UnmappedIdPasswordEncoder extends AbstractValidatingPasswordEncoder {

		@Override
		protected String encodeNonNullPassword(String rawPassword) {
			throw new UnsupportedOperationException("encode is not supported");
		}

		@Override
		protected boolean matchesNonNull(String rawPassword, String prefixEncodedPassword) {
			String id = extractId(prefixEncodedPassword);
			if (id != null && !id.isBlank()) {
				throw new IllegalArgumentException(String.format(NO_PASSWORD_ENCODER_MAPPED, id));
			}
			if (prefixEncodedPassword != null && !prefixEncodedPassword.isBlank()) {
				int start = prefixEncodedPassword.indexOf(DelegatingPasswordEncoder.this.idPrefix);
				int end = prefixEncodedPassword.indexOf(DelegatingPasswordEncoder.this.idSuffix, start);
				if (start < 0 && end < 0) {
					throw new IllegalArgumentException(NO_PASSWORD_ENCODER_PREFIX);
				}
			}
			throw new IllegalArgumentException(String.format(MALFORMED_PASSWORD_ENCODER_PREFIX,
					DelegatingPasswordEncoder.this.idPrefix, DelegatingPasswordEncoder.this.idSuffix));
		}

	}

}

View on GitHub (pinned to 96852e8860)