Hello community, I am currently chasing a bug in my character sheet, and am not sure if it is a me-bug or a roll20-bug, so I would like some confirmation on the way setAtttrs works. My understanding is the following: if you have on('change:test',function fnTest(){ ... }); And in another routine setAttrs({ "test" : value }, {silent :true}); The fnTest should not trigger. Is it the correct interpretation ? Is the syntax to set the silent option the right one ? ( I saw this syntax on another post, the code I started with had "silent" instead of {silent :true}, and I also tried {"silent" :true} None of those syntax gave me the expected result. Is anyone using this with success and could provide an example ? Now a bit more explanation on my use case, so that maybe someone has an idea what happens if it is not a bug from roll20. In the Earthdawn ruleset, each attribute has a value (3-18, similar to DnD), and a Step (equivalent to the attribute modifier in DnD) that affects the roll that will be done. The value is just for character creation/evolution, but I keep the traceability of how we came to this value so we have, and we are supposed to only edit this value: So I have Attrib-Dex-Curr = Attrib-Dex-Orig + Attrib-Dex-Race + Attrib-Dex-Increases (for the value set at creation, race bonuses and increases when levelling) Attrib-Dex-Step = a function of Attrib-Dex-Curr. Normally, you are only allowed to update Attrib-Dex-Orig, Attrib-Dex-Race and Attrib-Dex-Increases and the rest will be calculated by the sheet-workers. BUT (and that's where I need the "silent" option to avoid a circular loop), for NPC creation, it is more practical to be able to edit directly Attrib-Dex-Step, and in this case another sheetworker function will backfill the right value in Attrib-Dex-Orig to keep the data consistency). This is how it looks like: var updateAttribCurr = function fnUpdateAttribCurr( attrib, silent ) { /// ... I removed here the code to create the string of each attribute depending on the attribute it is called on getAttrs( [ curr, race, orig, inc, adjust ], function gaUpdateAttribCurr(values) { let vals = {}; vals[ curr ] = getInt( values, race) + getInt( values, orig) + getInt( values, inc) + getInt( values, adjust ); if( !silent ) setAttrsLog( vals ); else setAttrsLog( vals, "silent", function saUpdateAttribCurr() { //Call manually other updates for stuff that depend on Attrib-Dex-Step because you don't want to trigger again backfillOrigAttrib } }); } }); }; // End updateAttribCurr() on("change:attrib-dex-orig change:attrib-dex-race change:attrib-dex-increases", fn(_.bind(updateAttribCurr, {}, "Dex"))); var backfillOrigAttrib = function fnBackfillOrigAttrib( attrib ) { // I removed the parts to build the strings for the name of each variable getAttrs( [ step, orig, race, inc, adjust, adjuststep ], function gabackfillOrigAttrib(values) { var t = ((getInt( values, step) - getInt( values, adjuststep) -2) * 3) + 2 - (getInt( values, inc) + getInt( values, race) + getInt( values, adjust)); var cval = getInt( values, orig); if( cval < (t - 1) || (t + 1) < cval) { let vals = {}; vals [ orig ] = t; setAttrsLog( vals, "silent" ); updateAttribCurr( attrib, true); } }); }; // End backfillOrigAttrib() on("change:dex-step", fn(function ocDexStep(eventInfo) { if( eventInfo.sourceType === "player" ) backfillOrigAttrib( "Dex" ); })); The behaviour that I expect is that if I change Attrib-Dex-Step by a user action (the test eventInfo.sourceType === "player" is another safeguard against a circular loop), backfillOrigAttrib will recalculate the right value for Attrib-Dex-Orig so that the whole dataset is consistent, and write it in silent mode to avoid updateAttribCurr to trigger, and have an infinite loop of update between the 2 functions. Still there are some "depending values" that also have to be upddated so updateAttribCurr will be called manually with a "silent" flag that will inform it to make the necessary updates "with caution" and to not reupdate Attrib-Dex-Step. This corresponds to these lines of code setAttrsLog( vals, "silent" ); updateAttribCurr( attrib, true); The behaviour that I have in the console is updateAttribCurr is launched twice, one of them being the manual call, and the second being triggered by Attrib-Dex-Orig being updated. Nota : setAttrsLog is an internal function that is just the same thing as setAttrs, with just a few commented out lines in case I want to send everything to the console. Any idea/recommendation ?