openimsdk/open-im-server · error
user not found: %s
Error message
user not found: %s
What it means
FriendDB2Pb converts a friend DB row to its protobuf representation, which requires embedding the friend's user profile. It loads the user via getUsers; if the user map lacks the FriendUserID entry, the friend row points to a user record that no longer exists, so it fails with 'user not found: %s'.
Source
Thrown at pkg/common/convert/friend.go:48
func FriendPb2DB(friend *sdkws.FriendInfo) *model.Friend {
dbFriend := &model.Friend{}
err := datautil.CopyStructFields(dbFriend, friend)
if err != nil {
return nil
}
dbFriend.FriendUserID = friend.FriendUser.UserID
dbFriend.CreateTime = timeutil.UnixSecondToTime(friend.CreateTime)
return dbFriend
}
func FriendDB2Pb(ctx context.Context, friendDB *model.Friend, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (*sdkws.FriendInfo, error) {
users, err := getUsers(ctx, []string{friendDB.FriendUserID})
if err != nil {
return nil, err
}
user, ok := users[friendDB.FriendUserID]
if !ok {
return nil, fmt.Errorf("user not found: %s", friendDB.FriendUserID)
}
return &sdkws.FriendInfo{
FriendUser: user,
CreateTime: friendDB.CreateTime.Unix(),
}, nil
}
func FriendsDB2Pb(ctx context.Context, friendsDB []*model.Friend, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (friendsPb []*sdkws.FriendInfo, err error) {
if len(friendsDB) == 0 {
return nil, nil
}
var userID []string
for _, friendDB := range friendsDB {
userID = append(userID, friendDB.FriendUserID)
}
users, err := getUsers(ctx, userID)View on GitHub (pinned to 175a7bb067)
Solutions
- Clean up orphaned friend rows: DELETE FROM friends WHERE friend_user_id NOT IN (SELECT user_id FROM users)
- Restore the missing user record if it should exist (from backup)
- Add cascade delete on user removal so friend rows are purged together
- Make getUsers/convert tolerant: skip and log missing users instead of failing the whole list
Example fix
// before
user, ok := users[friendDB.FriendUserID]
if !ok { return nil, fmt.Errorf("user not found: %s", friendDB.FriendUserID) }
// after
user, ok := users[friendDB.FriendUserID]
if !ok {
log.ZWarn(ctx, "friend user missing, skipping", nil, "userID", friendDB.FriendUserID)
return nil, nil // or filter upstream
} Defensive patterns
Strategy: validation
Validate before calling
// detect orphaned friend rows before converting orphanSQL := `SELECT f.friend_user_id FROM friends f LEFT JOIN users u ON u.user_id = f.friend_user_id WHERE u.user_id IS NULL` rows, _ := db.Query(orphanSQL) // clean up or restore these users first
Try / catch
info, err := convert.FriendDB2Pb(ctx, friendDB)
if err != nil {
if strings.HasPrefix(err.Error(), "user not found:") {
log.ZWarn(ctx, "orphan friend row, skipping", err)
return nil // skip instead of failing whole list
}
return err
} Prevention
- Enable cascade delete of friend rows when a user is removed
- Run a periodic orphan-record reconciliation job
- Avoid manual deletes from the users table without cleaning friends
When it happens
Trigger: The friends table contains a row whose FriendUserID has no corresponding row in the users table when the friend list is fetched/converted.
Common situations: Manual DB cleanup deleted users but not their friend links; failed/partial cascade delete of a user account; data import/migration inconsistency; replication lag in read replicas.
AI-assisted analysis of openimsdk/open-im-server@175a7bb067 (2026-09-04).
Data as JSON: /api/errors/ed63bc6581e614cd.
Report an issue: GitHub.