Loading...
Loading...
12 production footguns ranked by severity. Data loss, exploits, memory leaks, mobile perf.
npx skill4agent add tabooharmony/roblox-brain roblox-sharp-edgesEvery entry here represents a real production footgun that has caused data loss, exploits, crashes, or hours of debugging in Roblox games.Severity Levels:
- Critical - Data loss, security breach, or revenue loss. Fix before shipping.
- High - Server instability, degraded experience, or exploit surface. Fix in current sprint.
- Medium - Correctness bugs, performance issues, or dev confusion. Fix before scale.
- Low - Code quality, maintainability, or minor timing issues. Fix when convenient.
MarketplaceService.ProcessReceiptPurchaseGrantedPurchaseGranted:Connect()RBXScriptConnection:Disconnect()PlayerRemovinglocal Players = game:GetService("Players")
local Trove = require(game.ReplicatedStorage.Packages.Trove)
local playerTroves: { [Player]: typeof(Trove.new()) } = {}
local function onPlayerAdded(player: Player)
local trove = Trove.new()
playerTroves[player] = trove
trove:Connect(player.CharacterAdded, function(character)
local humanoid = character:WaitForChild("Humanoid")
trove:Connect(humanoid.Died, function()
task.wait(3)
player:LoadCharacter()
end)
end)
end
local function onPlayerRemoving(player: Player)
local trove = playerTroves[player]
if trove then
trove:Clean()
playerTroves[player] = nil
end
end
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)local lastFire: { [Player]: number } = {}
local COOLDOWN = 0.1
AttackRemote.OnServerEvent:Connect(function(player: Player, targetId: number)
local now = os.clock()
if lastFire[player] and now - lastFire[player] < COOLDOWN then return end
lastFire[player] = now
-- process attack
end)game:BindToClose()task.spawnStreamingMinRadiusStreamingTargetRadiusModelStreamingModerequire()WaitForChildtask.waitlocal CombatSystem = {}
function CombatSystem:Init()
-- WaitForChild is safe here (called by bootstrap, not during require)
self._remotes = game.ReplicatedStorage:WaitForChild("Remotes", 10)
end
function CombatSystem:Start()
-- Connect events after all modules are Init'd
end
return CombatSystem:Init():Start()#tbl[3] = nil#tblniltable.remove()for _, v in tbl dofor i = 1, #tblwait()task.wait()spawn()task.spawn()delay()task.delay()WaitForChild(name)nillocal folder = ReplicatedStorage:WaitForChild("Weapons", 10)
if not folder then
warn("[Init] Weapons folder not found after 10s")
return
end\d%d%\|*?-%d\d%w\w%s\s%.\..-.*?%%%local functionfunctionA()helperB()helperB-- BAD: helperB is nil when functionA runs
local function functionA()
helperB() -- ERROR: attempt to call a nil value
end
local function helperB()
print("I'm a helper")
end
-- GOOD: helper declared first
local function helperB()
print("I'm a helper")
end
local function functionA()
helperB() -- works
endlocal functionB -- forward declare
local function functionA()
functionB()
end
function functionB() -- note: no 'local' (already declared above)
functionA()
endCRITICAL (fix before shipping):
SE-1 DataStore session locking → Use ProfileStore
SE-2 Client-side currency → Server-authoritative only
SE-3 ProcessReceipt order → Grant THEN PurchaseGranted
HIGH (fix in current sprint):
SE-4 Undisconnected events → Trove pattern (RbxUtil)
SE-5 RemoteEvent flooding → Per-player rate limiter
SE-6 BindToClose 30s timeout → Parallel saves with task.spawn
MEDIUM (fix before scale):
SE-7 Mobile part count → StreamingEnabled + <10K parts
SE-8 Yielding in module require → Init/Start lifecycle pattern
SE-9 Table # with nil gaps → table.remove or explicit length
SE-11 Infinite yield WaitForChild → Always pass timeout parameter
SE-13 Local function order → Callees above callers (no hoisting)
LOW (fix when convenient):
SE-10 Deprecated wait/spawn/delay → task.wait/spawn/delay
SE-12 String patterns vs regex → %d not \d, % not \