{"record":{"id":"7dad2f2e120f9bee","repo":"spring-projects/spring-security","slug":"encoded-password-does-not-look-like-bcrypt-x","errorCode":null,"errorMessage":"Encoded password does not look like BCrypt: X","messagePattern":"Encoded password does not look like BCrypt: X","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java","lineNumber":136,"sourceCode":"\n\tprivate String getSalt() {\n\t\treturn BCrypt.gensalt(this.version.getVersion(), this.strength, this.random.get());\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\tif (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {\n\t\t\tthis.logger.warn(\"Encoded password does not look like BCrypt\");\n\t\t\treturn false;\n\t\t}\n\t\treturn BCrypt.checkpw(rawPassword.toString(), encodedPassword);\n\t}\n\n\t@Override\n\tprotected boolean upgradeEncodingNonNull(String encodedPassword) {\n\t\tMatcher matcher = this.BCRYPT_PATTERN.matcher(encodedPassword);\n\t\tif (!matcher.matches()) {\n\t\t\tthrow new IllegalArgumentException(\"Encoded password does not look like BCrypt: \" + encodedPassword);\n\t\t}\n\t\tint strength = Integer.parseInt(matcher.group(2));\n\t\treturn strength < this.strength;\n\t}\n\n\t/**\n\t * Stores the default bcrypt version for use in configuration.\n\t *\n\t * @author Lin Feng\n\t */\n\tpublic enum BCryptVersion {\n\n\t\t$2A(\"$2a\"),\n\n\t\t$2Y(\"$2y\"),\n\n\t\t$2B(\"$2b\");\n","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java#L118-L154","documentation":"upgradeEncodingNonNull re-checks an existing encoded password against the BCrypt pattern ($2a/$2y/$2b followed by rounds and a 53-char hash portion) before parsing its strength. If the stored value does not match the pattern, the library throws IllegalArgumentException, because it cannot determine the rounds of a non-BCrypt value.","triggerScenarios":"Calling encoder.upgradeEncoding(storedValue) with a value that is not a BCrypt hash — e.g. a plaintext password, a SHA-256 hex digest, a null-ish/empty string, or a hash produced by another PasswordEncoder — where BCRYPT_PATTERN.matcher(...).matches() fails.","commonSituations":"Migrating from another encoder (DelegatingPasswordEncoder with mixed stored formats); database column truncated so the hash is cut short; storing a password with whitespace or a wrong column's data.","solutions":["Only call upgradeEncoding with values previously produced by BCryptPasswordEncoder (verify with a $2[aby]$ pattern first).","Fix data migrations so the column contains full 60-char BCrypt hashes.","Use DelegatingPasswordEncoder so non-BCrypt legacy hashes are handled by their own encoder/upgrade logic instead of this path."],"exampleFix":"// before\nif (encoder.upgradeEncoding(stored)) { ... } // throws on legacy hashes\n// after\nif (stored != null && stored.matches(\"\\\\$2[aby]\\\\$\\\\d{2}\\\\$.{53}\") && encoder.upgradeEncoding(stored)) { ... }","handlingStrategy":"type-guard","validationCode":"private static final Pattern BCRYPT = Pattern.compile(\"\\\\A\\\\$2[aby]?\\\\$\\\\d{2}\\\\$[./A-Za-z0-9]{53}\\\\z\");\nboolean looksLikeBcrypt(String s) { return s != null && BCRYPT.matcher(s).matches(); }","typeGuard":"boolean isBcryptHash(String s) { return s != null && s.startsWith(\"$\") && BCRYPT_PATTERN.matcher(s).matches(); }","tryCatchPattern":"try { needUpgrade = encoder.upgradeEncoding(stored); } catch (IllegalArgumentException e) { needUpgrade = true; // non-BCrypt legacy values always need re-encoding on next login }","preventionTips":["Use DelegatingPasswordEncoder so each stored format is handled by its own encoder.","Never truncate hash columns; keep at least 60 chars for BCrypt.","Check format before calling upgradeEncoding on data of unknown provenance."],"tags":["bcrypt","invalid-argument-format","spring-security","password-hashing"],"backgroundTag":"invalid-argument-format","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}