Instance:GetAttributeChangedSignal() Fires in an Infinite Loop with Instance:SetAttribute()

Repro:
AttributeDoubleFire.rbxl (40.5 KB)

Instance:GetAttributeChangedSignal() fires in a constant loop when Instance:SetAttribute() is within it.

Code:

-- vars
local AttributeName = "Fire"

-- attribute events
script:GetAttributeChangedSignal(AttributeName):Connect(function()
	print("Attribute fired!")
	if script:GetAttribute(AttributeName) then
		script:SetAttribute(AttributeName, false)
	else
		script:SetAttribute(AttributeName, true)
	end
end)

-- startup
script:SetAttribute(AttributeName, false)

This code sets an attribute to false once, and then proceeds to keep firing GetAttributeChangedSignal as if it were in an infinite while-loop.

Expected Behavior:
I expect Instance:GetAttributeChangedSignal() to not fire in an infinite while-loop from using the provided Code above.

Actual Behavior:
image
The attribute fires more than once, and it will keep firing until it reaches infinity.

Issue Area: Engine
Impact: Low
Frequency: Constantly
Date First Experienced: 2023-04-10 08:27:00 (UTC -08:00)
Date Last Experienced: 2023-04-14 12:02:00 (UTC -08:00)

Why should the provided code not act that way? It seems expected to me.

The problem that you are running into could be an implementation problem rather than an engine problem. Depending on what you are trying to achieve, you could solve this with a debounce or a separation of action from state.

6 Likes

It’s 100% their implementation, their code is functioning exactly the way they wrote it. OP shouldn’t be doing anything that fires the same changed event they’re observing.

Every time the attribute is changed, it sets an attribute based on the inverse condition, which would just have the current handler queue another event, ad infinitum, starting with the first set. It can help a lot to do some self code review first or reach out on the support categories.

2 Likes

This is not a bug at all. This is completely intentional. It seems that you might need to add a debounce to not have it fire infinitely

2 Likes

I am unable to reproduce this issue with the code you have shared. When I run it in Studio I see that the re-entrancy error triggers at 6 when SignalBehavior is immediate and 79 when it is deferred.

Deferred:

Immediate:

4 Likes

I now see why it is most likely an implementation problem. My use case was to make a custom class Tool by setting attributes on a Model with an “Activated” attribute that fires true every click, then set it back to false within the Instance:GetAttributeChangedSignal() event.

Adding a debounce to the provided code will call print("Attribute fired!") twice.

Thank you for your input, this report is now expected behavior. A good take-away is the provided code shows attributes are instantly responsive and its behavior should remain unchanged. This is solved by using RemoteEvent, RemoteFunction, BindableEvent, or BindableFunction instead.

Thank you @VitalWinter @colbert2677 @hunterk1241 @WallsAreForClimbing .

3 Likes