Why isn't this working? HTML row in a grid that repeats four more times (save_1, save_2, etc.): < input type = 'number' min = '1' max = '20' value = '13' title = '@{save_1_base}' name = 'attr_save_1_base' /> < input type = 'number' min = '-20' max = '0' value = '0' title = '@{save_1_mod}' name = 'attr_save_1_mod' /> < input type = 'number' readonly title = '@{save_1}' name = 'attr_save_1' /> Sheet worker that is triggered by a change to a global save bonus modifier elsewhere on the sheet: // Save Bonus // Apply save bonus to all saving throw modifiers // Then, recalculate all saving throw targets on('change:class_save_bonus', function(eventInfo) { const output = {}; getAttrs(['class_save_bonus', 'save_1_base', 'save_2_base', 'save_3_base', 'save_4_base', 'save_5_base'], function(values) { const saveBonus = parseInt(values.class_save_bonus) || 0; for (var i = 1; i < 6; i++) { theBaseValue = parseInt(values['save_' + i + '_base']) || 0; output['save_' + i + '_mod'] = -saveBonus; output['save' + i] = Math.max(1, theBaseValue - saveBonus); console.log('output: ' + output['save' + i]); }; }); setAttrs(output); }); (Note that in this game system, everything is a "throw", meaning bonuses are subtracted from the target, then rolled against.) The function iterates through the five sets of fields to calculate and set values. I'm able to update these fields from a sheet worker, individually, if/when the player changes the Base or the Mod value. I've validated that all getAttrs values are being collected, as expected. I can see, via the console.log, that the output array contains the correct, calculated values. I've taken out the readonly attribute on the third <input> field just in case but same issue. Issue: Neither the 'save_X_mod' or 'save_X' input fields are updating in the UI. Is this some kind of UI bug or asynchronous issue?