SetNetworkOwner Incorrectly Errors When Connected To InstanceAdded Signal

Reproduction Steps

Can be reproduced by running the following code on an empty place:

game:GetService("CollectionService"):GetInstanceAddedSignal("NewTag"):Connect(function(Part)
	print("Grounded?:", Part:IsGrounded())
	print("Parent: ", Part.Parent)
	Part:SetNetworkOwner()
end)

workspace.ChildAdded:Connect(function(Part)
	print("Grounded?:", Part:IsGrounded())
	print("Parent: ", Part.Parent)
	Part:SetNetworkOwner()
end)

local Part = Instance.new("Part")
game:GetService("CollectionService"):AddTag(Part, "NewTag")
Part.Parent = workspace


Expected Behavior

The part’s network owner is set to the server when parented to workspace.

Actual Behavior

An “Network Ownership API cannot be called on Anchored parts or parts welded to Anchored parts.” error occurs on both connections when the part is added, despite the part being a descendant of workspace and IsGrounded() returning false. This error does not occur in the following scenarios:

  • SetNetworkOwner is called immediately after reparenting the part, with no event connections involved.
  • SetNetworkOwner is called inside task.spawn, either immediately after parenting or inside one of the event handlers.
  • SetNetworkOwner is called inside task.defer, either immediately after parenting or inside one of the event handlers.

Workaround

Using task.defer inside the event handler prevents the error from occurring.

Issue Area: Engine
Issue Type: Other
Impact: Moderate
Frequency: Constantly
Date First Experienced: 2022-08-25 00:08:00 (+01:00)
Date Last Experienced: 2022-08-26 00:08:00 (+01:00)

1 Like

InstanceAdded(part) is called just before DescendantAdded(part) is fired on the DataModel.

Part is added to WorldRoot internal World (and Assembly created) just before AncestorChanged is fired on the part.

This is the worst part about synchronous callbacks. You have to know which internal changes happen on which callbacks specifically in what order. The Parent setter has a lot…

  • DescendantRemoving (InstanceRemoved just before DataModel.DescendantRemoving)
  • Parent property field set
  • ChildRemoved
  • DescendantAdded (InstanceAdded just before DataModel.DescendantAdded)
  • ChildAdded
  • AncestorChanged (added to internal World just before AncestorChanged fired)
  • Parent property changed events

I hate it, but changing this would definitely be a breaking change. Reluctantly, not a bug.

As a workaround try adding Part.AncestorChanged:Wait() before SetNetworkOwner().

Deferred or AncestryDeferred SignalBehavior would also make this unnecessary, since all the internal changes, like adding to internal World, would happen before any of these signals are fired.

10 Likes