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.
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.
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.
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.