'RunService:IsClient()' is sometimes unreliable in-game

I personally haven’t run into this issue myself, but I have gotten over 110k+ error reports of mine caused by RunService:IsClient() returning false on client sided code over the past week. The code is in a module script being ran from a local script.

I have no way to reliably reproduce this issue as it only seems to happen to some people, but this issue only started happening about a few weeks ago.

Expected behavior

I expect RunService:IsClient() to return true 100% of the time when running under client context

1 Like

That’s so weird, I have used RunService:IsClient() on ModuleScripts and it’s always worked.

1 Like

I quickly made this somewhat reliable recreation of IsClient. Try giving this a shot.

Usage:
IsClient(DetectStudio: boolean?)
If true is passed as the first argument (DetectStudio) to the function, it will return false when ran inside studio, no matter if it is in the client or the server, and will only return true if ran in the Roblox client. If false or nothing is passed as the first argument, it will detect if it is being ran in the client or in the server and will return the correct boolean value even if it is being ran in Studio.

-- If the game.NetworkServer check ever stops working, you can check if Players.LocalPlayer exists instead. If it does, it's being ran in client. If not, it's being ran in server.

function IsClient(DetectStudio: boolean?): boolean -- If true is passed as the first argument, it will return false whenever the function is ran inside studio, no matter if it is in a LocalScript in the client or a server script.
	local function ProtectedIndex(IndexInto: {} | Instance, IndexValue: string)
		if IndexInto == nil then
			return false
		end
		
		local Success, Response = pcall(function()
			return IndexInto[IndexValue]
		end)
		
		if not Success then
			return false, Response
		else
			return true, Response
		end
	end
	
	if DetectStudio then
		local Attempt, Value = ProtectedIndex(game, "StudioService") 
		
		if not Attempt then
			if Value == "The current thread cannot access 'StudioService' (lacking capability Plugin)" then
				return false -- return false because studioservice exists, this is being ran in studio
			end
		end
		
		if Attempt then
			return false -- return false because studioservice exists, this is being ran in studio
		end
	end
	
	local Attempt, Value = ProtectedIndex(game, "NetworkServer")
	
	if not Attempt then
		if string.match(Value, "The current thread cannot access") ~= nil then
			return false -- return false as networkserver exists, it means this is running in the server
		end
		
		return true -- return true as networkserver doesnt exist, it means this is running in the client
	end
	
	return false -- return false as networkserver exists, it means tjis is running in the server
end