···373373// InsertUserIfNotExists inserts a user record only if it doesn't already exist.
374374// Used by non-profile collections to avoid unnecessary writes during backfill.
375375func InsertUserIfNotExists(db DBTX, user *User) error {
376376+ // Clear handle from any other DID that currently holds it.
377377+ // In ATProto, a handle belongs to exactly one DID at a time —
378378+ // if a new DID claims this handle, the old association is stale.
379379+ _, _ = db.Exec(`UPDATE users SET handle = did WHERE handle = ? AND did != ?`,
380380+ user.Handle, user.DID)
381381+376382 _, err := db.Exec(`
377383 INSERT INTO users (did, handle, pds_endpoint, avatar, last_seen)
378384 VALUES (?, ?, ?, ?, ?)
···383389384390// UpsertUser inserts or updates a user record
385391func UpsertUser(db DBTX, user *User) error {
392392+ // Clear handle from any other DID that currently holds it.
393393+ // In ATProto, a handle belongs to exactly one DID at a time —
394394+ // if a new DID claims this handle, the old association is stale.
395395+ _, _ = db.Exec(`UPDATE users SET handle = did WHERE handle = ? AND did != ?`,
396396+ user.Handle, user.DID)
397397+386398 _, err := db.Exec(`
387399 INSERT INTO users (did, handle, pds_endpoint, avatar, last_seen)
388400 VALUES (?, ?, ?, ?, ?)
···398410// UpsertUserIgnoreAvatar inserts or updates a user record, but preserves existing avatar on update
399411// This is useful when avatar fetch fails, and we don't want to overwrite an existing avatar with empty string
400412func UpsertUserIgnoreAvatar(db DBTX, user *User) error {
413413+ // Clear handle from any other DID that currently holds it.
414414+ // In ATProto, a handle belongs to exactly one DID at a time —
415415+ // if a new DID claims this handle, the old association is stale.
416416+ _, _ = db.Exec(`UPDATE users SET handle = did WHERE handle = ? AND did != ?`,
417417+ user.Handle, user.DID)
418418+401419 _, err := db.Exec(`
402420 INSERT INTO users (did, handle, pds_endpoint, avatar, last_seen)
403421 VALUES (?, ?, ?, ?, ?)