5 min read

Source: Roblox Creator Hub · CC BY 4.0 · View source · Code samples: MIT Imported 2026-10-03. Formatting adapted for this site.

InstanceHandle

The InstanceHandle data type is a weak reference to an Instance that might not be available locally. Use it to point at an instance that hasn't arrived yet, could stream out, or exists only on another machine, and to pick that instance up once it's there.

A handle always refers to the same target. To reach that target, call InstanceHandle:Wait(), which yields until the instance is available:

local Workspace = game:GetService("Workspace")

local part = Instance.new("Part")
local target = Instance.new("Part")
target.Name = "TargetPart"
part.Parent = Workspace
target.Parent = Workspace

part:SetAttribute("Target", target)

local handle = part:GetAttribute("Target")
local instance = handle:Wait(10)
if instance then
	print("Target is", instance.Name) --> Target is TargetPart
end

Prefer InstanceHandle:Wait() with a timeout so your code runs as soon as the instance shows up. Use InstanceHandle:Get() when you want an immediate answer and can handle nil, such as in code that runs every frame.

Examples

Attributes

Attributes use InstanceHandle instead of an ordinary instance reference. Since the handle itself is never nil, you can tell apart an attribute that is missing from one where the instance is not currently present:

local part = Instance.new("Part")

print(part:GetAttribute("Target")) --> nil

part:SetAttribute("Target", InstanceHandle.new(nil))
local handle = part:GetAttribute("Target")
print(handle:Get()) --> nil

InstanceHandle attributes also appear in Instance:GetAttributes(), which can't hold nil values.

Instance:GetAttributeChangedSignal() fires when the attribute is set to a new target or removed. It does not fire when the target streams in or out, or when the target is destroyed. To react to availability, combine the signal with InstanceHandle:Wait():

local part = Instance.new("Part")

part:GetAttributeChangedSignal("Target"):Connect(function()
	local handle = part:GetAttribute("Target")
	if not handle then
		return
	end

	local instance = handle:Wait(10)
	if instance then
		print("New target:", instance.Name)
	end
end)
Remote events

Pass a handle through a RemoteEvent to give the receiver a reference to an instance it doesn't have yet. The receiver waits for the instance instead of getting nothing.

In a server script, call this function with your remote event, the receiving player, and the model to send:

local function sendHandle(remoteEvent: RemoteEvent, player: Player, spawnedModel: Model)
	local handle = InstanceHandle.new(spawnedModel)
	remoteEvent:FireClient(player, handle)
end

In a client script, call this function with the same remote event to start listening before the server sends the handle:

local function receiveHandles(remoteEvent: RemoteEvent)
	remoteEvent.OnClientEvent:Connect(function(handle)
		local model = handle:Wait(10)
		if model then
			print("Received", model.Name)
		end
	end)
end

Ownership

A handle acts as a weak reference. It never keeps its target alive. The lifetime of the target is controlled by the rest of your experience: its parent and any ordinary references your scripts hold that prevent it from being garbage collected.

InstanceHandle:Get() and InstanceHandle:Wait() return an ordinary instance reference, which does keep the instance alive for as long as you hold it.

A handle is otherwise lightweight and needs no cleanup.

Equality

Two handles are equal when they refer to the same target, even if you created them separately and even after the target is gone. All empty handles are equal to each other. Each handle is still a distinct value.

local instance1 = Instance.new("Part")
local instance2 = Instance.new("Part")

local a = InstanceHandle.new(instance1)
local b = InstanceHandle.new(instance1)
local c = InstanceHandle.new(instance2)

print(a == b) --> true
print(a == c) --> false

Equality depends on target identity, so it still works when the target isn't currently available.

Constructors

NameType / ReturnsDescription
InstanceHandle.newReturns a new InstanceHandle referring to the given Instance.

InstanceHandle.new

The handle refers to the instance weakly and doesn't extend its lifetime.

local target = Instance.new("Part")
target.Name = "TargetPart"

local handle = InstanceHandle.new(target)
local empty = InstanceHandle.new(nil)

print(handle:Get()) --> TargetPart
print(empty:Get()) --> nil

Throws if instance is neither an Instance nor nil.

Parameters

NameTypeDefaultDescription
instanceInstance?The instance to refer to, or nil for an empty handle.

Methods

NameType / ReturnsDescription
InstanceHandle:GetInstance?Returns the referenced Instance if it's available locally, otherwise nil.
InstanceHandle:WaitInstance?Yields until the referenced Instance is available locally, then returns it.

InstanceHandle:Get

This method doesn't yield.

Get() returns nil when the handle is empty, when the target hasn't arrived on the client or server running the script, when the target is no longer available after streaming out, when the target has been garbage collected, and when the target isn't accessible to the calling script. Use InstanceHandle:Wait() to yield until the target arrives instead of checking once.

Returns

TypeDescription
Instance?The referenced instance, or nil if it isn't available locally.

InstanceHandle:Wait

Returns nil if the handle is empty, the target is inaccessible to the calling script, or timeout elapses first. Wait() returns immediately when the target is already available and when the handle is empty.

Pass a timeout whenever you can't guarantee that the target reaches the caller. A target that's destroyed before it replicates leaves a handle that never resolves, and a Wait() with no timeout never returns. A pending wait can't be cancelled: task.cancel() stops your coroutine, but the wait isn't unregistered until the target arrives or the timeout elapses.

timeout must be a number greater than zero. Throws if timeout is zero, negative, or not a number.

Parameters

NameTypeDefaultDescription
timeoutnumber?How long to wait, in seconds. Must be greater than zero. Waits indefinitely when omitted.

Returns

TypeDescription
Instance?The referenced instance, or nil if the handle is empty, the target is inaccessible to the calling script, or timeout elapses first.