
在 go 中直接用 == 比较自定义错误(如 bcrypt.errmismatchedhashandpassword)失败,通常源于多个包路径下重复定义了相同名称的错误变量,导致底层 *errors.errorstring 指针不一致。
在 go 中直接用 == 比较自定义错误(如 bcrypt.errmismatchedhashandpassword)失败,通常源于多个包路径下重复定义了相同名称的错误变量,导致底层 *errors.errorstring 指针不一致。
Go 的错误比较陷阱常让开发者困惑:明明错误信息完全一致,err == bcrypt.ErrMismatchedHashAndPassword 却始终为 false。根本原因在于 Go 错误本质是接口值(error),而 errors.New() 创建的是指向不同内存地址的 *errors.errorString 实例——即使内容相同,指针也不同。
在你的案例中,问题出在混用了两个历史版本的 bcrypt 包:
- FindAccount 函数导入的是已废弃的旧包:
import "code.google.com/p/go.crypto/bcrypt" // ❌ 已归档,不再维护
- 调用方却使用了官方维护的新包:
import "golang.org/x/crypto/bcrypt" // ✅ 当前标准库推荐
两者各自定义了同名错误变量:
var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password")由于属于不同包,这两个变量在编译后位于不同内存地址(正如日志中 0xc2080290b0 与 0xc2080291e0 所示),因此指针比较必然失败。
✅ 正确解决方案:统一使用官方维护的包
将所有导入替换为:
import "golang.org/x/crypto/bcrypt"
并确保项目中无残留的旧包引用(可通过 go mod graph | grep bcrypt 检查依赖图)。升级后,错误比较即可正常工作:
if err == bcrypt.ErrMismatchedHashAndPassword {
return nil, EmailPasswordInvalidError{}
}⚠️ 更健壮的替代方案:使用 errors.Is()(推荐)
即使未来引入包装错误(如 fmt.Errorf("auth failed: %w", err)),errors.Is() 也能穿透多层包装进行语义匹配:
if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
return nil, EmailPasswordInvalidError{}
}该函数自 Go 1.13 引入,是现代 Go 错误处理的标准实践。
? 总结建议
- 始终优先使用 golang.org/x/crypto/bcrypt,避免任何 code.google.com/p/go.crypto/... 的遗留引用;
- 在条件判断中,用 errors.Is(err, targetErr) 替代 err == targetErr,提升可维护性与兼容性;
- 运行 go mod tidy 后检查 go.sum,确认 bcrypt 相关依赖仅存在唯一版本;
- 对关键认证逻辑,建议补充单元测试覆盖密码错误、邮箱不存在、哈希格式异常等边界场景。










