Considering warlock's spellcasting and slot-tracking isn't quite same as all the other classes, and warlock's have had share of own bugs, I highly suspect this is tied to your character being a warlock. Roll20 last updated the Companion API exactly one year ago , so there is liekly room for improvement on the API script based on one year of sheet updates alone. Being a Warlock as long as he can upcast the spell to level 4 it is
fine, it is only when he has a 3rd level spell that can't be upcast is
there a problem. This is interesting, and sound like warlock upcasting spells that normally can't be upcast might be breaking the companion API. This is the parts of the API that handles spellslots (cut out non-spell stuff): on ( 'chat:message' , function ( msg ) { // ROLL LISTENERS else if ( msg . playerid . toLowerCase () != "api" && msg . rolltemplate ) { var cnamebase = msg . content . split ( "charname=" )[ 1 ]; var cname = cnamebase ? cnamebase . replace ( '}}' , '' ). trim () : ( msg . content . split ( "{{name=" )[ 1 ]|| '' ). split ( "}}" )[ 0 ]. trim (); var player = getObj ( "player" , msg . playerid ); var character = undefined ; if ( cname ) { list = findObjs ({ name : cname , type : 'character' },{ caseInsensitive : true }); if ( list != undefined ){ if ( list . length > 1 ){ log ( 'Duplicate character names, process cancelled' ); return ; } } character = list [ 0 ] } if ([ "spell" ]. indexOf ( msg . rolltemplate ) > - 1 ) { if ( msg . content . indexOf ( "{{level=" ) > - 1 && character && state . FifthEditionOGLbyRoll20 . spelltracking != "off" ) { handleslotspell ( msg , character , player ); } } } }); var handleslotspell = function ( msg , character , player ) { var spellslot = (( msg . content . split ( "{{level=" )[ 1 ]|| '' ). split ( "}}" )[ 0 ]|| '' ). split ( " " )[ 1 ]; var ritual = msg . content . indexOf ( "{{ritual=1}}" ) > - 1 ? true : false ; var innate = msg . content . indexOf ( "{{innate=}}" ) > - 1 ? false : true ; if ( spellslot === "0" || spellslot === "cantrip" || spellslot === "npc" || ritual || innate ) { return ; } resolveslot ( msg , character , player , spellslot ); }; var resolveslot = function ( msg , character , player , spellslot ) { var charslot = findObjs ({ type : 'attribute' , characterid : character . id , name : "lvl" + spellslot + "_slots_expended" }, { caseInsensitive : true })[ 0 ]; if (! charslot ) { charslot = createObj ( "attribute" , { name : "lvl" + spellslot + "_slots_expended" , current : "0" , max : "" , characterid : character . id }); //var charslot = findObjs({type: 'attribute', characterid: character.id, name: "lvl" + spellslot + "_slots_expended"}, {caseInsensitive: true})[0]; } var charslotmax = findObjs ({ type : 'attribute' , characterid : character . id , name : "lvl" + spellslot + "_slots_total" }, { caseInsensitive : true })[ 0 ]; if (! charslotmax ) { charslotmax = createObj ( "attribute" , { name : "lvl" + spellslot + "_slots_total" , current : "0" , max : "" , characterid : character . id }); //var charslotmax = findObjs({type: 'attribute', characterid: character.id, name: "lvl" + spellslot + "_slots_total"}, {caseInsensitive: true})[0]; } var spent = parseInt ( charslot . get ( "current" ), 10 ); charslot . set ({ current : Math . max ( spent - 1 , 0 )}); var wtype = getAttrByName ( character . id , "wtype" ); var wtoggle = getAttrByName ( character . id , "whispertoggle" ); let displayName = player . get ( 'displayname' ); let playerName = "" ; if ( state . FifthEditionOGLbyRoll20 . spelltracking === "player" ) playerName = ( displayName === undefined || playerIsGM ( player . get ( "_id" ))) ? undefined : `/w " ${ player . get ( 'displayname' ) } " ` ; var sendPlayerMsg =( playerName !== "" || wtype === "" || ( wtype === "@{whispertoggle}" && wtoggle === "" ) || ( wtype === "?{Whisper?|Public Roll,|Whisper Roll,/w gm }" && msg . type === "general" )); var sendGMMsg =( playerName !== "" || wtype === "/w gm " || ( wtype === "@{whispertoggle}" && wtoggle === "/w gm " ) || ( wtype === "?{Whisper?|Public Roll,|Whisper Roll,/w gm }" && msg . type === "whisper" )); if ( spent > 0 ) { if ( state . FifthEditionOGLbyRoll20 . spelltracking != "quiet" ) { if ( sendPlayerMsg ) { if ( playerName !== undefined ) sendChat ( msg . who , playerName + "<div class='sheet-rolltemplate-simple' style='margin-top:-7px;'><div class='sheet-container'><div class='sheet-label' style='margin-top:5px;'><span style='display:block;'>SPELL SLOT LEVEL " + spellslot + "</span><span style='display:block;'>" + Math . max ( spent - 1 , 0 ) + " OF " + charslotmax . get ( "current" ) + " REMAINING</span></div></div></div>" ); } if ( sendGMMsg ) { sendChat ( msg . who , "/w gm <div class='sheet-rolltemplate-simple'><div class='sheet-container'><div class='sheet-label' style='margin-top:5px;'><span style='display:block;'>SPELL SLOT LEVEL " + spellslot + "</span><span style='display:block;'>" + Math . max ( spent - 1 , 0 ) + " OF " + charslotmax . get ( "current" ) + " REMAINING</span></div></div></div>" ); } } } else { if ( sendPlayerMsg ) { if ( playerName !== undefined ) sendChat ( msg . who , playerName + "<div class='sheet-rolltemplate-simple' style='margin-top:-7px;'><div class='sheet-container'><div class='sheet-label' style='margin-top:5px;'><span style='display:block;'>SPELL SLOT LEVEL " + spellslot + "</span><span style='display:block; color:red;'>ALL SLOTS EXPENDED</span></div></div></div>" ); } if ( sendGMMsg ) { sendChat ( msg . who , "/w gm <div class='sheet-rolltemplate-simple'><div class='sheet-container'><div class='sheet-label' style='margin-top:5px;'><span style='display:block;'>SPELL SLOT LEVEL " + spellslot + "</span><span style='display:block; color:red;'>ALL SLOTS EXPENDED</span></div></div></div>" ); } } };