Calling Destroy from a different Destroy causes a locked property error in immediate signal mode

Running the following code in specifically Immediate signal mode will throw an error about the parent being locked.

local Part = Instance.new("Part")

Part.AncestryChanged:Connect(function(_, p)
  if not p then
    Part:Destroy()
  end
end)

Part.Parent = workspace
Part:Destroy()

This code sample may not be perfect because why am I destroying an instance thats already being destroyed, but the issue is more with tables that track Instances, this code sample may better show the issue:

function Object.Destroy(self: Object)
	local private = ObjectBindings[self]
	local inst = self.Instance::Instance
	if not private or not inst then return end
	local impl = Implementors[self]

	Implementors[self] = nil
	impl.Objects[inst] = nil
	ObjectBindings[self] = nil
	InstanceBindings[inst] = nil

	if self.Component then
		self.Component:Destroy()
	end

	clearMemoryState(private.Weak)
	if impl.Destroying then impl.Destroying(self) end
	clearMemoryState(private.Weak)
end

This function is triggered internally when a collectionservice tag is disposed of, destroying an object or parenting to nil unallocates a tag, so it triggers a full destruction.

There’s no way to tell if this was triggered by a Destroy call, so we have to assume its not. It will throw the error if it was destroyed elsewhere.

Expected behavior

If an instance is already being destroyed, then future Destroy calls should not try to move the parent, even if Destroy was called mid-Destroy (through an event).

The parent should only be set to nil once.

This error also does not arrise in deferred signalling mode for obvious reasons.

Workaround (works for me)

I found wrapping the destroy call in a task.defer fixes the issue in immediate mode, obviously I cant identify which signalling mode is being used, but it would be nice to read this for frameworks, though this is not for this bug report

2 Likes

Thanks for the report! We’ll follow up when we have an update for you.