roblox-teleport — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited roblox-teleport (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Official sources (always check these for the latest):
TeleportService moves players between places and servers. The modern entry point is TeleportAsync; the older Teleport/TeleportPartyAsync/TeleportToPlaceInstance/TeleportToPrivateServer/TeleportToSpawnByName methods are deprecated and should not be used for new work.
Cross-reference:
Activate when:
TeleportInitFailed).A universe (experience) can contain multiple places. Each place has its own PlaceId; the universe has a UniverseId (exposed as game.GameId). One place is the primary place — the one players join from the Roblox app's main entry point.
game.PlaceId is the current place; game.GameId is the universe ID (sometimes called UniverseId).TeleportService:TeleportAsync(placeId, players, teleportOptions?) is the single unified method. It can teleport to a different place, a specific server, or a reserved server. Server-only — calling from the client errors.
--!strict
local TeleportService = game:GetService("TeleportService")
local Players = game:GetService("Players")
local DESTINATION_PLACE_ID = 1234567890
local function teleportToPlace(player: Player)
local options = Instance.new("TeleportOptions")
-- options.ServerInstanceId = "..." -- to target a specific server
-- options.ReservedServerAccessCode = "..." -- to join a reserved server
-- options.ShouldReserveServer = true -- to create+join a new reserved server
options:SetTeleportData({ reason = "lobby_choice", mode = "classic" })
local ok, result = pcall(function()
return TeleportService:TeleportAsync(DESTINATION_PLACE_ID, { player }, options)
end)
if not ok then
warn("TeleportAsync failed:", tostring(result))
return false
end
-- result is a TeleportAsyncResult with info about the destination
return true
endTeleportAsync call.These combinations error with "Incompatible Parameters":
ReservedServerAccessCode + ServerInstanceIdShouldReserveServer + ServerInstanceIdShouldReserveServer + ReservedServerAccessCode| Property | Purpose |
|---|---|
ServerInstanceId | Target a specific server by JobId. |
ReservedServerAccessCode | Join an existing reserved server by its access code. |
ShouldReserveServer | Create a new reserved server and teleport into it. |
SetTeleportData(data) / GetTeleportData() | Pass data to the destination (retrieved via GetLocalPlayerTeleportData). |
When TeleportOptions is passed, TeleportAsync returns a TeleportAsyncResult with information about the final teleport destination. Useful for confirming where a player ended up.
TeleportService.TeleportInitFailed fires when a teleport fails to start. Connect to it to show a retry UI. Failure reasons include invalid place ID, empty player list, non-Player instances, wrong teleport options type, client-called TeleportAsync, and conflicting parameters.
TeleportService.TeleportInitFailed:Connect(function(player: Player, options: TeleportOptions, reason: Enum.TeleportResult, errMsg: string, placeId: number, groupName: string)
warn(`Teleport failed for {player.Name}: {reason} - {errMsg}`)
-- Show retry UI, log, etc.
end)TeleportService:ReserveServerAsync(placeId) (server-only, yields) returns an access code plus a PrivateServerId. Players can only join a reserved server via that access code — they can't join through normal matchmaking.
local isReserved = game.PrivateServerId ~= "" and game.PrivateServerOwnerId == 0.PrivateServerId is constant across server instances for the same access code; JobId is not.PrivateServerId. Use game.MatchmakingType to differentiate.--!strict
local TeleportService = game:GetService("TeleportService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
-- Reserve once, persist the code, reuse it
local store = DataStoreService:GetDataStore("ReservedServerCodes")
local code
local ok, saved = pcall(function() return store:GetAsync("DungeonRoom_1") end)
if ok and typeof(saved) == "string" then
code = saved
else
local reserveOk, newCode = pcall(function() return TeleportService:ReserveServerAsync(game.PlaceId) end)
if reserveOk then
code = newCode
pcall(function() store:SetAsync("DungeonRoom_1", code) end)
end
end
local function sendToDungeon(player: Player)
if not code then return end
local options = Instance.new("TeleportOptions")
options.ReservedServerAccessCode = code
options:SetTeleportData({ room = 1 })
pcall(function()
TeleportService:TeleportAsync(game.PlaceId, { player }, options)
end)
endTwo mechanisms, with different security profiles:
TeleportOptions:SetTeleportData / GetLocalPlayerTeleportDataSet on the source server (via TeleportOptions), retrieved on the destination client via TeleportService:GetLocalPlayerTeleportData(). Client-only retrieval. Can carry any serializable value.
Security: exploiters can spoof teleport data. Send sensitive data (currency, entitlements) through DataStoreService instead, and use teleport data only for non-sensitive hints (selected game mode, cosmetic loadout preference).
SetTeleportSetting / GetTeleportSettingClient-only, stored locally, preserved across teleports within the same game. Good for client-side preferences (crouch state, UI tab) that don't need server trust. Also spoofable — use server-side validation for anything that affects gameplay.
TeleportService:SetTeleportGui(gui) (client) sets a ScreenGui shown during teleport. TeleportService:GetArrivingTeleportGui() (client, at the destination) retrieves it so you can parent it to PlayerGui and run your own transition. The teleport GUI does not cross into a different game (universe), and does not persist across multiple teleports — set it before each one.
--!strict
-- At the destination, in ReplicatedFirst
local TeleportService = game:GetService("TeleportService")
local Players = game:GetService("Players")
local ReplicatedFirst = game:GetService("ReplicatedFirst")
local customLoadingScreen = TeleportService:GetArrivingTeleportGui()
if customLoadingScreen then
local playerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
ReplicatedFirst:RemoveDefaultLoadingScreen()
customLoadingScreen.Parent = playerGui
task.wait(5)
customLoadingScreen:Destroy()
endTeleporting from your experience to another experience owned by others fails by default. To enable cross-experience teleportation, follow the steps in the official Teleport between places doc. Within your own universe (cross-place), teleports work without extra setup.
TeleportService:PromptExperienceDetailsAsync(player, universeId) (client-only, yields) prompts the player with experience details and a Join button. If the player is ineligible to join the target experience, the button is disabled. The join button is always disabled during Studio playtesting.
A lobby place collects players; when enough are ready, TeleportAsync sends them to a game place. Use MessagingService to coordinate across lobby servers if you have multiple lobbies.
For instanced dungeons or private matches: ReserveServerAsync → persist the access code in a DataStore keyed by match ID → teleport the party with ReservedServerAccessCode → players arrive in an isolated server.
Maintain a MemoryStoreService sorted map of active servers (keyed by JobId, value = player count / mode / ping). The lobby lists entries; when a player picks one, TeleportOptions.ServerInstanceId = jobId and TeleportAsync. Update the sorted map from each game server on join/leave. This scales better than MessagingService for many servers.
For complex matchmaking (skill-based, party queues), use an external HTTP service as the matchmaker. It returns a placeId + JobId or a fresh reserved-server access code; the lobby then TeleportAsyncs the party. See roblox-networking for the HttpService security model and roblox-open-cloud for in-experience Open Cloud calls.
All matchmaking must respect the 50-player-per-`TeleportAsync` limit; split larger groups into multiple calls.
The robust pattern for moving player state between places:
UpdateAsync (see roblox-datastores). Then teleport.PlayerAdded, load the profile via GetAsync (consider UseCache=false for a fresh read if the save was recent).TeleportAsync.TeleportService does not work during Studio playtesting. You must publish the experience and test teleports in the Roblox application. Plan for this in your testing workflow — teleport logic can only be verified live.
BindToClose on the source server can race with the teleport — finish saves before triggering teleports when data is critical.CustomizedTeleportUI is deprecated and does nothing.PrivateServerId.TeleportAsync a group across experiences.Players.PlayerAdded → check player.FollowUserId → GetPlayerPlaceInstanceAsync(followId) → TeleportToPlaceInstance. Note GetPlayerPlaceInstanceAsync returns JobId, not the reserved-server access code, so it won't work to follow a friend into a reserved server.scripts/TeleportHelper.lua — a server-side helper that wraps TeleportAsync with pcall, options, and a simple reserved-server-code cache.TeleportAsync (server) + TeleportOptions.ReserveServerAsync once, persist the code, reuse it.TeleportInitFailed for retry UI.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.