Roblox Studio error

Can only register ProcessReceipt callback on server

What Can only register ProcessReceipt callback on server means in Roblox Studio, the two things that cause it with the code that triggers each, and the fix for each one.

Updated 5 November 20263 min readErrorLuauWritten by

In short

MarketplaceService.ProcessReceipt is the function Roblox calls after a player buys a developer product, so your game can give them what they paid for. Roblox only accepts it from the server, because the client is the player's own device and could grant itself anything. A LocalScript (or a ModuleScript a LocalScript required) that sets it gets this error. Keep the purchase prompt in the LocalScript and move the receipt handling to one Script in ServerScriptService.

  • Client: MarketplaceService:PromptProductPurchase(player, productId) when the button is pressed.
  • Server: one Script sets MarketplaceService.ProcessReceipt, gives the item and returns PurchaseGranted only once it is really given.
  • The same applies to the newer MarketplaceService:BindReceiptHandler.
On this page
  1. What it means
  2. Causes and fixes
  3. Fix it with RoCode
  4. Find the line
  5. Questions
  6. Sources

What the message means

Output (example)
Players.Builderman.PlayerGui.Shop.BuyCoins.LocalScript:12: Can only register ProcessReceipt callback on server

Other forms of the same message:

  • BindReceiptHandler can only be called from the server

Until a receipt is answered with Enum.ProductPurchaseDecision.PurchaseGranted, Roblox treats the purchase as not delivered and tries again later, for example when the player next joins. A receipt handler that never runs, because it was set in a LocalScript, means players pay and get nothing in that session.

The PromptProductPurchaseFinished event on the client only says the purchase window closed. Do not give items from it: the server receipt is the only reliable signal.

Causes and fixes

Two things cause this error in practice. Each one below has the smallest code that triggers it, then the fix.

The shop button's LocalScript handles the receipt

It feels natural to keep the prompt and the reward together in the button's script. The prompt part is allowed there; the receipt part is not.

Breaks LocalScript in the BuyCoins button in StarterGui named BuyCoins
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")

local COINS_PRODUCT_ID = 0000000 -- your developer product ID
local button = script.Parent :: TextButton

button.Activated:Connect(function()
	MarketplaceService:PromptProductPurchase(Players.LocalPlayer, COINS_PRODUCT_ID)
end)

-- Can only register ProcessReceipt callback on server
MarketplaceService.ProcessReceipt = function(_receiptInfo)
	return Enum.ProductPurchaseDecision.PurchaseGranted
end

Delete the receipt part from the LocalScript, keep only the prompt, and handle receipts in a server Script like the one below.

Fixed Script in ServerScriptService named Receipts
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")

local COINS_PRODUCT_ID = 0000000 -- your developer product ID

local function grantCoins(player: Player): boolean
	local leaderstats = player:FindFirstChild("leaderstats")
	local coins = leaderstats and leaderstats:FindFirstChild("Coins")
	if coins and coins:IsA("IntValue") then
		coins.Value += 100
		return true
	end
	return false -- not ready yet: Roblox will try this receipt again later
end

MarketplaceService.ProcessReceipt = function(receiptInfo: { [string]: any }): Enum.ProductPurchaseDecision
	local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
	if player and receiptInfo.ProductId == COINS_PRODUCT_ID then
		local ok, granted = pcall(grantCoins, player)
		if ok and granted == true then
			return Enum.ProductPurchaseDecision.PurchaseGranted
		end
	end
	return Enum.ProductPurchaseDecision.NotProcessedYet
end

This is the smallest working handler. For a real game, record each PurchaseId in a data store together with the grant, so a receipt delivered twice is never granted twice: the developer products guide builds that version step by step.

A shared module in ReplicatedStorage sets it, and a LocalScript requires it

A Shop ModuleScript in ReplicatedStorage that both the client and the server require runs its top-level code on whichever side requires it. If it sets ProcessReceipt at the top, the client's copy fails.

Breaks ModuleScript in ReplicatedStorage named Shop
local MarketplaceService = game:GetService("MarketplaceService")

local Shop = {}
Shop.COINS_PRODUCT_ID = 0000000

function Shop.prompt(player: Player)
	MarketplaceService:PromptProductPurchase(player, Shop.COINS_PRODUCT_ID)
end

-- runs on the client too when a LocalScript requires this module:
-- Can only register ProcessReceipt callback on server
MarketplaceService.ProcessReceipt = function(_receiptInfo)
	return Enum.ProductPurchaseDecision.NotProcessedYet
end

return Shop

Keep shared modules free of receipt code. Shared data (product IDs, prices) can stay in ReplicatedStorage; the receipt handler lives in ServerScriptService and is the only code that sets ProcessReceipt.

Fixed ModuleScript in ReplicatedStorage named Shop
local MarketplaceService = game:GetService("MarketplaceService")

local Shop = {}
Shop.COINS_PRODUCT_ID = 0000000

function Shop.prompt(player: Player)
	MarketplaceService:PromptProductPurchase(player, Shop.COINS_PRODUCT_ID)
end

return Shop

Only one script should set ProcessReceipt. Setting it again from another server Script silently replaces the first handler, so products handled there stop being granted.

Fix it in your place

Paste the error, RoCode fixes it in your place

Copy the line from Output and paste it here. RoCode opens the script it names in the place you have open in Studio, works out which of the causes above it is, and edits the code there instead of handing you a snippet to paste.

For example: Players.Builderman.PlayerGui.Shop.BuyCoins.LocalScript:12: Can only register ProcessReceipt callback on server

RoCode is an AI agent for Roblox Studio: it reads the scripts in the place you have open, finds the line and edits it there, and every change is an undo point. It runs on Windows or macOS with a desktop browser. Free to start, with free credits every day.

How to find the line that causes it

  1. The path in the message names the script. A path through PlayerGui, PlayerScripts or a character means a LocalScript.
  2. Search your place for ProcessReceipt and BindReceiptHandler with Ctrl+Shift+F (Find All). There should be exactly one place that sets it, in ServerScriptService.

Questions

Where do I put ProcessReceipt in Roblox?

In one Script in ServerScriptService. LocalScripts only show the purchase prompt with PromptProductPurchase.

Can I give the item from PromptProductPurchaseFinished?

No. That event only says the window closed, and on the client a cheater can fake it. Grant developer products from the server's receipt handler.

What should ProcessReceipt return?

Enum.ProductPurchaseDecision.PurchaseGranted only after the item has really been given (and saved), and NotProcessedYet in every other case, so Roblox tries the receipt again later.

Can RoCode fix it?

Yes. Paste the Output line into RoCode. It reads your open place, moves the receipt handling into one server Script and keeps the prompt in your button.

Sources and further reading

How the code was checked: every block marked Fixed passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 5 November 2026), and every block marked Breaks compiles and fails only when it runs. The message wording was checked against the message strings in Roblox Studio itself (macOS build installed 2 November 2026) and the MarketplaceService reference on 5 November 2026. A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.

More Roblox errors

All Roblox Studio errors

Stop hunting errors line by line.

RoCode reads the scripts in your open place, fixes the error where it happens and tells you what it changed. Free every day, no card required.