Accessing server data too fast for the client?

Hello, I am trying to make a basic UI shop system with map skins and items. However, something does not really work and I do not know why. I am a beginner at this, so please bare with me. Also, please tell me if this way of doing things for this kind of shop system is OK/not! I have:

  • a datstore module (I used Supphi’s datastore module);
  • a module script used for storing info about the skins/items;
  • a server script handling purchases;
  • a localscript in StarterPlayerScripts that should update the UI for all of the skins.

I say should because it only updates 2 skins out of them all and I think there’s a race condition where I try to access the data of the player faster than I should? But I do not know how I can improve this/if this is the issue.

Here is the client script that should update the UI:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local Player = Players.LocalPlayer
local PlayerGui = Player:WaitForChild("PlayerGui")

local SkinAndItemData = require(ReplicatedStorage.MapSkinsAndItemsData)
local PurchaseEvent = ReplicatedStorage.NewRemoteEvents:WaitForChild("PurchaseSkinEvent")
local UpdatePlayerInventory = ReplicatedStorage.NewRemoteEvents:WaitForChild("UpdatePlayerInventory")

local shopUI = PlayerGui:WaitForChild("ShopUI")
local skinShopUI = shopUI.BGFrame.ScrollingFrame:WaitForChild("MapSkins")
local skinContainer = skinShopUI:WaitForChild("Container")

local currentPlayerData = {}
local connectedButtons = {} -- track connected buttons


local function UpdateSkinFrame(frame, skinName)
	local skinData = SkinAndItemData:GetSkinData(skinName)
	if not skinData then return end

	local isOwned = currentPlayerData.MapSkins 
		and currentPlayerData.MapSkins.Skins 
		and currentPlayerData.MapSkins.Skins[skinName] 
		and currentPlayerData.MapSkins.Skins[skinName].Owned

	local isDefault = skinData.isDefault

	frame.SkinName.Text = skinData.displayName
	frame.SkinDescription.Text = skinData.description

	frame.BuyButton.Visible = not isOwned and not isDefault
	frame.EquipButton.Visible = isOwned
	frame.PriceDisplay.Visible = not isOwned and not isDefault

	if frame.PriceDisplay.Visible then
		local priceTextLabel = frame.PriceDisplay:FindFirstChildWhichIsA("TextLabel")
		local priceImage = frame.PriceDisplay:FindFirstChild("ImageLabel")

		if skinData.gamepassId then
			priceTextLabel.Text = skinData.gamepassPrice
			priceTextLabel.TextColor3 = Color3.fromRGB(85, 255, 127)
			priceImage.Image = skinData.robuxImage
		elseif skinData.price then
			priceTextLabel.Text = tostring(skinData.price)
			priceTextLabel.TextColor3 = Color3.fromRGB(255, 255, 0)
			priceImage.Image = skinData.coinImage
		end
	end


	if not connectedButtons[frame.BuyButton] then
		connectedButtons[frame.BuyButton] = true
		frame.BuyButton.Activated:Connect(function()
			PurchaseEvent:FireServer(skinName)
		end)
	end
end


local function UpdateAllSkins()
	for skinName, _ in pairs(SkinAndItemData:GetAllSkins()) do
		local frame = skinContainer:FindFirstChild(skinName)
		if frame then
			UpdateSkinFrame(frame, skinName)
		end
	end
end


UpdatePlayerInventory.OnClientEvent:Connect(function(playerData)
	currentPlayerData = playerData
	UpdateAllSkins()
end)



shopUI:GetPropertyChangedSignal("Enabled"):Connect(function()
	if shopUI.Enabled then
		UpdateAllSkins()
	end
end)

here is my server script:

local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local SkinAndItemData = require(ReplicatedStorage.MapSkinsAndItemsData)
local DataStoreModule = require(game.ServerScriptService.MainModule)

local PurchaseEvent = ReplicatedStorage.NewRemoteEvents:WaitForChild("PurchaseSkinEvent")
local UpdatePlayerInventory = ReplicatedStorage.NewRemoteEvents:WaitForChild("UpdatePlayerInventory")

local function GrantSkin(playerData, skinName)
	playerData.MapSkins.Skins[skinName] = {
		Owned = true,
		Equipped = false
	}
end


local function ProcessPurchase(player, skinName)
	local dataStore = DataStoreModule.find("PlyrData", player.UserId)
	if not dataStore or not dataStore.Value then
		return false, "Data not loaded"
	end

	local playerData = dataStore.Value
	local skinData = SkinAndItemData:GetSkinData(skinName)
	if not skinData then return false, "Invalid skin" end

	if playerData.MapSkins.Skins[skinName] and playerData.MapSkins.Skins[skinName].Owned then
		return false, "Already owned"
	end


	if skinData.gamepassId then
		local ownsPass = false
		pcall(function()
			ownsPass = MarketplaceService:UserOwnsGamePassAsync(player.UserId, skinData.gamepassId)
		end)

		if not ownsPass then
			MarketplaceService:PromptGamePassPurchase(player, skinData.gamepassId)
			return false, "Gamepass required"
		end
	end


	if skinData.price and not skinData.gamepassId then
		if playerData.Currencies.Coins < skinData.price then
			return false, "Not enough coins"
		end
		playerData.Currencies.Coins -= skinData.price
	end

	GrantSkin(playerData, skinName)
	UpdatePlayerInventory:FireClient(player, playerData)
	return true, skinName
end


PurchaseEvent.OnServerEvent:Connect(function(player, skinName)
	local success, result = ProcessPurchase(player, skinName)
end)

There are no errors, it just does not work as expected.