In short
A RemoteEvent has two ends. The client (a LocalScript) calls FireServer and listens with OnClientEvent. The server (a Script) calls FireClient or FireAllClients and listens with OnServerEvent. This error means one of them is in the wrong kind of script. Move that line to the other side, or swap it for its partner.
- Server Script to one player:
remote:FireClient(player, ...). To everyone:remote:FireAllClients(...). - LocalScript to the server:
remote:FireServer(...). The server receives the player as the first argument automatically.
What the message means
ServerScriptService.Shop:8: FireServer can only be called from the client
Other forms of the same message:
FireClient can only be called from the serverFireAllClients can only be called from the serverOnServerEvent can only be used on the serverOnClientEvent can only be used on the clientInvokeServer can only be called from the client
Which side a script runs on depends on its type and place: a Script in ServerScriptService runs on the server; a LocalScript in StarterPlayerScripts or StarterGui runs on each player's client. A Script whose RunContext is Client also runs on the client.
The same rule applies to RemoteFunctions: InvokeServer from the client, InvokeClient from the server.
Causes and fixes
Three things cause this error in practice. Each one below has the smallest code that triggers it, then the fix.
- FireServer in a server Script
- OnServerEvent in a LocalScript
- FireClient or FireAllClients in a LocalScript
FireServer in a server Script
The server is already the server; it cannot fire to itself. This often happens when code from a LocalScript is pasted into a Script to "make it work for everyone".
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local showMessage = ReplicatedStorage:WaitForChild("ShowMessage")
-- in a server Script: FireServer can only be called from the client
showMessage:FireServer("Round starting!")
From the server, use FireAllClients (everyone) or FireClient(player, ...) (one player). The LocalScript listens with OnClientEvent.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local showMessage = ReplicatedStorage:WaitForChild("ShowMessage") :: RemoteEvent
showMessage:FireAllClients("Round starting!")
OnServerEvent in a LocalScript
A LocalScript cannot receive what clients send to the server. Connecting to OnServerEvent there fails straight away.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyItem = ReplicatedStorage:WaitForChild("BuyItem")
-- in a LocalScript: OnServerEvent can only be used on the server
buyItem.OnServerEvent:Connect(function(player, itemName)
print(player, itemName)
end)
Put the OnServerEvent handler in a Script in ServerScriptService. Keep only the FireServer call in the LocalScript.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyItem = ReplicatedStorage:WaitForChild("BuyItem") :: RemoteEvent
buyItem.OnServerEvent:Connect(function(player: Player, itemName: unknown)
if typeof(itemName) ~= "string" then
return
end
print(player.Name, "wants to buy", itemName)
end)
Always check what a client sends: its type, and whether that player is allowed to do it. The RemoteEvents guide shows the full pattern.
FireClient or FireAllClients in a LocalScript
Clients cannot message each other directly. A LocalScript that tries to tell other players something with FireAllClients fails.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local cheer = ReplicatedStorage:WaitForChild("Cheer")
-- in a LocalScript: FireAllClients can only be called from the server
cheer:FireAllClients("GG!")
Send it to the server with FireServer, and let a server Script pass it on to everyone with FireAllClients.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local cheer = ReplicatedStorage:WaitForChild("Cheer") :: RemoteEvent
-- client: ask the server
cheer:FireServer("GG!")
-- everyone hears it once the server passes it on
cheer.OnClientEvent:Connect(function(fromName: string, text: string)
print(fromName .. ": " .. text)
end)
And on the server: cheer.OnServerEvent:Connect(function(player, text) cheer:FireAllClients(player.DisplayName, text) end), after checking that text is a short string. Text shown to other players must go through Roblox's text filtering.
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.
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
- Open Output (Window menu, or the Script tab), clear it with Ctrl+K (⌘K on Mac), and press Play.
- Find the first red line. It starts with the script's place in the Explorer and the line number, such as
ServerScriptService.Shop:12:. - Click the red line. Studio opens that script with the cursor on the failing line.
- Read the lines between
Stack BeginandStack Endunder it: they list the calls that led there, newest first, so you can see which function called the broken one. - Still not sure which value is wrong? Add
print(typeof(x), x)just above the line, or set a breakpoint on it and read the values in the Watch window.
Questions
Which side is my script on?
A Script in ServerScriptService or Workspace runs on the server. A LocalScript in StarterPlayerScripts, StarterCharacterScripts, StarterGui or a Tool runs on the player's client. In Output, server lines are marked Server and client lines Client.
Why does the server get the player automatically?
Roblox adds the player who called FireServer as the first argument of OnServerEvent, so a client cannot pretend to be someone else.
Where should the RemoteEvent be?
In ReplicatedStorage, so both the server and every client can see it.
Can RoCode fix it?
Yes. Paste the Output line into RoCode. It moves each RemoteEvent call to the right kind of script in your open place and adds the matching handler on the other side.
Sources and further reading
- Roblox Creator Docs: remote events and callbacks
- Roblox Creator Docs: RemoteEvent
- Roblox Creator Docs: text filtering
How the code was checked: every block marked Fixed passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 23 October 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 of 13 October 2026) on 23 October 2026. A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.