Having an issue with qb-phone call history showing up wrong after players transfer characters. The new character will have call logs from their previous character, sometimes with duplicate entries where the source/target don't match up right.
I can see in the MySQL logs that there are concurrent UPDATE statements happening on the phone_calls table around the same time as the character transfer. Getting some 'Duplicate entry' warnings in the server console too.
The reproduction seems to be tied to this query running during character transfer:
MySQL.query('UPDATE phone_calls SET source = @src WHERE target = @target', {['@src'] = PlayerData.charinfo.phone, ['@target'] = targetNumber})
This runs concurrently with the TransferCharacter() function without any locking mechanism. The stateBag playerId doesn't match between the call metadata and the active character sometimes.
Anyone run into this before? Not sure if I need to implement some kind of mutex lock around the phone operations during transfers or handle the transaction differently.
CloudStrife · 31/08/2026
@cloud_strife When you say the character transfer is happening, are you calling TransferCharacter() from client-side or is this purely server-side? Also, does your phone number get reassigned as part of the same server event that handles the character data swap, or is that happening separately?
SniperWolf · 31/08/2026
@sniper_wolf Character transfer is purely server-side through my custom system. The phone number reassignment happens in the same server event as the character data swap, but I'm not handling them atomically - they're separate function calls within the same event handler. That might be part of the problem since the phone operations could be hitting the DB while the character identity is still changing.
Sign in to reply.
CloudStrife · 31/08/2026
@cloud_strife That's exactly the race condition. Your phone number reassignment and character data swap are separate function calls within the same event handler, but they're not atomic. While the character identity is swapping, your concurrent phone call updates are still using the old PlayerData context with the new character's phone number.
The MySQL UPDATE is grabbing the source phone from the transitioning PlayerData.charinfo.phone while the target character transfer is still completing. This writes the new character's phone number into call records that should belong to the old character's history.
You need to wrap both the character transfer and phone number reassignment in a transaction with proper locking. Queue any pending call updates until the identity swap completes, then validate the character ID matches the call metadata before processing.
SniperWolf · 01/09/2026