Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

API Infinite Loop Detected

1757799741

Edited 1757799827
I've been using Roll20 for quite a while now, and the API for several years of that. The API has always had a bit of a problem with detecting infinite loops, necessitating restarts several times per night (but never at any sort of predictable interval, suggesting that no particular script was causing it). The last few weeks, however, I have not been able to use the API at all. I restart it over and over and over and it reaching an infinite loop error 95% of the time, with the remaining 5% just being a brief few minutes of working before hitting an infinite loop error. I have not changed the scripts I have used in quite some time, and don't use very many to begin with. Typically, I use libTokenMarkers, TokenNameNumber, Ping Buddy, and two small custom scripts (that allow players to read information in a Loot entry or in the notes of an icon with a macro). Again, I have used these for quite some time with only some issues with infinite loops, it is only recently (with no script changes of any type whatsoever on my part) that I have started experiencing this. If it matters, I have been continuously updating the custom character sheet I use for my game, including numerous sheetworkers (though I was of the understanding that is a separate system, and they still work just fine when the API shuts down). I have tried disabling them in various combinations, but so far the issue persists even with just one script active. For example, I currently have disabled everything except libTokenMarkers, which still immediately results in an infinite loop detected. It does not even fully activate from what I can tell; it reaches infinite loop status too quickly. Luckily, I do not rely on such scripts for core functionality, so it does not prevent my games, but it is very annoying. Is there any particular solution for this besides just not using the API? Perhaps a way to force the system to relax whatever factors it looks for to try and detect an infinite loop?  Thank you very much for any assistance.
1757800811

Edited 1757800885
Gauss
Forum Champion
Hi Alexander,  If you don't get a response from someone here you may want to re-post this in the Mods (API Scripts) forum where the API Script writers are more likely to see it.  I would also suggest posting a screenshot of your scripts and Github links to the code for any custom scripts.
If I don't hear anything, I'll try the other forum. For the moment, the custom scripts don't run out of github; I just have the small code saved in Roll20 itself. Both were made by others who helped out with specific requests in years past. The code for the smaller one, called Inspect GM Notes, is : on('ready',() => {   on('chat:message',(msg) => {     if('api' === msg.type && msg.content.match(/^!inspect/) ){       let content = msg.content.split(/\s+/);       let token=getObj('graphic',content[1]);       if(token){         let who = getObj('player',msg.playerid).get('displayname');         sendChat('',`/w "${who}" <div style="border:1px solid #999;border-radius:.5em;padding:.1em .5em; font-size: .6em;background-color:#eee; color:#333;">${unescape(token.get('gmnotes')).replace(/(?:[\n\r]+|<br\/?>)/g,'<br>')}</div>`);       }     }   }); }); The code for the second one, meant to help with loot, is: /* !Loot @{target|token_id} or !Loot @{selected|token_id} You must have an Ability on the character sheet named Loot. This will be printed out, will roll any inline rolls, and preserve paragraphs. */ var loot = loot || (function () {     'use strict';     const version = '0.2',         COMMAND = 'Loot', // this is the name you must use for the Ability, make sure capitalisation matches.         HPBAR = 1,  // this is the bar used for tracking HP         MARKER = 'trophy', // set the status marker used to show if treasure has been rolled for this token.         scriptName = 'Loot',         lastUpdate = 1545180290523,          divider = ' -|- ', // do not use same as newline         newline = '--',         random = '|',                  checkInstall = function () {             log('--| ' + scriptName + ' v' + version + ' |--   [' + (new Date(lastUpdate)) + ']');         },         processInlinerolls = function (msg) {             if (_.has(msg, 'inlinerolls')) {                 return _.chain(msg.inlinerolls)                     .reduce(function(previous, current, index) {                         previous['$[[' + index + ']]'] = current.results.total || 0;                         return previous;                     },{})                     .reduce(function(previous, current, index) {                         return previous.replace(index, current);                     }, msg.content)                     .value();             } else {                 return msg.content;             }         },                  getSpeaker = function(msg) {             let characters = findObjs({_type: 'character'});             let speaking;             characters.forEach(function(chr) { if(chr.get('name') == msg.who) speaking = chr; });                  let speaker = speaking ? 'character|'+speaking.id : 'player|'+msg.playerid;             return speaker;         },         handleInput = function (msg) {             if ('api' === msg.type && msg.content.match(`!format${COMMAND} `)) {                 let args = processInlinerolls(msg).split(divider); //);                 let lootBase = args[2].split(newline);                 let loot = [], i = 0;                 lootBase.forEach(row => {                     let item = row.split(random);                     loot[i] = item.length === 1 ? item : item[randomInteger(item.length -1)];                     i ++;                 });                 let output = `&{template:spell} {{name=${args[1].trim()}'s Treasure}} {{description=${loot.join('\n')}}}`;                 let speaker = args[3];                 sendChat(speaker,output);             } else if ('api' === msg.type && msg.content.match(`!${COMMAND} `)) {                  let id = msg.content.replace(`!${COMMAND}`,'').trim();                 let token = findObjs({_type: 'graphic', id: id})[0];                 let tname = token.get('name');                 if(!token) {                     sendChat(COMMAND,'Token Not Found');                     return;                 }                 if(token.get('status_' + MARKER)) {                     sendChat(COMMAND,`${tname} has already been looted.`);                     return;                 }                 let character = getObj('character', token.get('represents'));                             if(!character) {                     sendChat(COMMAND,`Character Not Found for token ${tname}.`);                     return;                 }                 let greenVal = token.get(`bar${HPBAR}_value`);                 if (isNaN(greenVal)) {                     sendChat(COMMAND,`${tname}'s Bar is not a number`);                 } else if (greenVal > 0) {                     sendChat(COMMAND,`${tname} is not yet defeated.`);                 } else {                     //let loot = getAttrByName(character.id, COMMAND, 'current');                     let loot = findObjs({_type: 'ability', characterid: character.id, name: COMMAND})[0];                     if(!loot) {                         sendChat(COMMAND,`Loot Not Found for ${tname}'s token.`);                     } else {                           let speaker = getSpeaker(msg);                         let action = loot.get('action').toString();                         let output = action.split('\n').join(newline);                         token.set('status_' + MARKER,true);                         sendChat(COMMAND, `!format${COMMAND} ${divider} ${tname} ${divider} ${output} ${divider} ${speaker}`); //                     }                 }                              }          },         registerEventHandlers = function () {             on('chat:message', handleInput);         };     return {         CheckInstall: checkInstall,         RegisterEventHandlers: registerEventHandlers     }; }()); on('ready', function () {     'use strict';     loot.CheckInstall();     loot.RegisterEventHandlers(); });
1757803039

Edited 1757803068
Here is a screenshot of my scripts. I currently have all but one disabled, which still results in the infinite loop. There is one script there I didn't mention, Rollable Table Macros, which I disabled some time ago for unrelated reasons, and just haven't deleted it.  Edit: Not sure if there is a way to make this higher res for easier viewing, or if that is necessary.