The problem is likely because you have your getAttrs and setAttrs functions inside a loop. This is very bad practice on roll20, due to the asynchronous nature of these functions and how slow they are (especially setAttrs). Part of it might also be that fact your change line is watching the whole repeating section, so every time a value is changed (by setAttrs) it triggers the worker again. It's best to be precise with your on(change) line, and make sure you target only the attributes you want to. Here's a quick edit of your code to solve both of thos issues: on ( 'sheet:opened change:repeating_rangedweapons:r_weapon_attack change:repeating_rangedweapons:r_weapon_burst change:repeating_rangedweapons:r_weapon_auto remove:repeating_rangedweapons' , function () { getSectionIDs ( 'rangedweapons' , function ( idarray ) { // first get the attribute names for all rows in put in one array const fieldnames = []; idarray . forEach ( id => fieldnames . push ( `repeating_rangedweapons_ ${ id } _R_weapon_attack` , `repeating_rangedweapons_ ${ id } _R_weapon_burst` , `repeating_rangedweapons_ ${ id } _R_weapon_auto` )); // use that array so you can do a single getAttrs statement getAttrs ( fieldnames , values => { // create a variable to hold all the attribute values you re going to create. const attr = {}; // now loop through the rows again idarray . forEach ( id => { let row = 'repeating_rangedweapons_' + id ; let ammo , attack = values [ ` ${ row } _R_weapon_attack` ], burst = values [ ` ${ row } _R_weapon_burst` ], auto = values [ ` ${ row } _R_weapon_auto` ]; if ( attack == '0' ) { //semi-auto ammo = - burst ; } else if ( attack == '-10' ) { // full-auto ammo = - auto ; } else { // anything else default to single fire ammo = - 1 ; } attr [ ` ${ row } _id` ] = id ; // id of the repeating element attr [ ` ${ row } _ammo` ] = ammo ; // number of ammo to use }); setAttrs ( attr ); }); }); }); Notice the structure: Immediately after getSectionIDs, loop through the entire repeating section, build an array of attribute names including the row id Then end the loop, and do getAttrs just once (not in loop) on the full set of attributes. create an attribute to hold the saved attribute values for a single setAttrs later. Now do a second loop, again through the repeating section, and calculate your attributes Then after that loop has ended, do the setAttrs. Your layout was very close, but this process of splitting into two loops to isolate the getAttrs and setAttrs functions is not intuitive. It's necessary because those functions are very slow, and dont happen linearly. Once the command is initiated, it goes off and does its own thing, and the rest of the code continues. This - combined with not being precise in your on(change) line, is what causes the lag you saw and the wrong values being set. PS: Note that attribute names on the event line must be in lower case always.