Hello,
I found a pretty simple way of instantly and reliably detecting Potassium on injection.
The detection takes advantage of Potassium’s console handling. Under the right conditions, you can make it hide an error that Roblox would normally add to the console, then check whether that error actually appeared.
As a side effect, the same behavior can also be used to send messages into Potassium’s console.
Showcase
Basic example
Here’s a small example showing the general idea:
-- LocalScript
local LogService = game:GetService("LogService")
local Players = game:GetService("Players")
local oldLogHistory = LogService:GetLogHistory()
-- Trigger an error, `error()` would work too, but LogService:Error
-- is nicer because it doesn't add traceback lines, therefore it
-- slightly avoids spamming the console with unnecessary traceback lines.
pcall(LogService.Error, LogService, "''")
task.wait() -- Wait for Roblox to update the console history
local newLogHistory = LogService:GetLogHistory()
-- If the error didn't appear in the new console history, it means Potassium likely intercepted it.
if #oldLogHistory == #newLogHistory then
-- Send a large error message to the Potassium console to hide the detection.
pcall(LogService.Error, LogService, "''" .. string.rep("detected\n", 4000))
Players.LocalPlayer:Kick("Potassium detected.")
end
Normally, the probe appears in LogService:GetLogHistory(). With Potassium injected, its output handling intercepts the error instead, so it never appears in the history. This works on Potassium’s default configuration.
Don’t use the example above as-is in a real anticheat; Roblox only keeps a limited number of console messages, so simply comparing the number of entries becomes unreliable once the history is full.
If you actually want to use the detection in your game, feel free to use a more reliable version below.
Reliable version
-- LocalScript
-- ReplicatedFirst / StarterPlayerScripts
const LogService = game:GetService("LogService")
const Players = game:GetService("Players")
const LocalPlayer = Players.LocalPlayer
const CheckInterval = 1
while task.wait(CheckInterval) do
-- Send a unique probe error to the console to check if Potassium intercepts it.
local currentProbeMessage = "''" .. math.random()
pcall(LogService.Error, LogService, currentProbeMessage)
task.wait()
local newLogHistory = LogService:GetLogHistory()
local found = false
for i = #newLogHistory, 1, -1 do
if newLogHistory[i].message == currentProbeMessage then
found = true
break
end
end
if not found then
-- Do not use the code below as-is in a real anticheat, exploiters
-- can intercept the `:Kick` function and bypass it. Replace the following
-- code with your own code, preferably a remote event that triggers player ban.
pcall(LocalPlayer.Kick, LocalPlayer, "Potassium user")
end
end
In my testing, these probe errors also haven’t shown up in the game’s Analytics error logs.
The implementation can obviously be taken further, but this should show the general idea.
Why does this happen?
Potassium filters errors caused by scripts executed through it. Those scripts have an empty name, so their tracebacks contain something like Script '', Line 1.
The problem is that its output handling looks for that empty script name when deciding whether an error belongs to Potassium. If we put the same '' into our own error, it gets mistaken for one of Potassium’s errors and filtered out before it reaches Roblox’s normal console history.
This is super easy for them to fix, which is the main reason I’m posting it instead of keeping it private or putting it into one of my anticheats. I still thought it was worth sharing because of how simple it is, and because it can detect Potassium without waiting for the user to execute anything.
If anyone manages to get a false positive with this, or finds another edge case I missed, let me know.
I will be releasing more stuff soon, so stay tuned.
I do Luau programming and Roblox anticheat work, including custom detections and general anticheat development. If you’re working on a game and need a gameplay programmer/engineer or an anticheat developer, feel free to contact me.