Stable variable cannot be discarded

Since a stable variable needs to change its structure and I no longer need the data inside,
I directly change the type and upgrade the request. The error is as follows:

Call failed:
Canister: ojpsk-siaaa-aaaam-adtea-cai
Method: getMobileApplastVersion (query)
"Status": "rejected"
"Code": "CanisterError"
"Message": "Error from Canister ojpsk-siaaa-aaaam-adtea-cai: Canister trapped: heap out of bounds.\nConsider gracefully handling failures from this canister or altering the canister to handle exceptions. See documentation: https://internetcomputer.org/docs/current/references/execution-errors#trapped"

After I renamed it, I compiled and installed it and reported the following error:

(unknown location): Compatibility error [M0169], stable variable mobileApplastVersion of previous type
  {
    var android : {apk : Text; notes : Text; store : Text; version : Text};
    var ios : {notes : Text; store : Text; version : Text}
  }
cannot be discarded; promote to type `Any` instead

How can I delete useless stable variables?
This is in a production environment.

1 Like

This is very bad. No matter how I adjust my code, I cannot upgrade the container program.
Currently, our project has a large number of users joining and has been widely recognized by users.
I hope to get strong support from the dfinity core team. Thank you!

root@iZt4ndh0vykzq4xl63g186Z:/home/dfinity/deltaContract# dfx canister install delta -m upgrade --ic
Please enter the passphrase for your identity: [hidden]
Decryption complete.
Upgrading code for canister delta, with canister ID ojpsk-siaaa-aaaam-adtea-cai
WARNING!
Stable interface compatibility check issued an ERROR for canister 'delta'.
Upgrade will either FAIL or LOSE some stable variable data.

(unknown location): Compatibility error [M0171], stable variable mobileApplastVersion changes mutability from previous type
  {
    var android : {apk : Text; notes : Text; store : Text; version : Text};
    var ios : {notes : Text; store : Text; version : Text}
  }
to new type 
  var
   {
     var android : {apk : Text; notes : Text; store : Text; version : Text};
     var ios : {notes : Text; store : Text; version : Text}
   }

Do you want to proceed? yes/No
yes
This canister requires an initialization argument.

Do you want to initialize the canister with this argument? [y/N]
y
Error: Failed to install wasm module to canister 'delta'.
Caused by: Failed during wasm installation call
Caused by: The replica returned a rejection error: reject code CanisterError, reject message Error from Canister ojpsk-siaaa-aaaam-adtea-cai: Canister trapped: heap out of bounds
Canister Backtrace:
obj_idx<0>
@buffer_size<r(notes:t,store:t,version:t)>
@buffer_size<Mr(notes:t,store:t,version:t)>
@buffer_size<r(android!:r(apk:t,notes:t,store:t,version:t),ios!:r(notes:t,store:t,version:t))>
@buffer_size<?r(android!:r(apk:t,notes:t,store:t,version:t),ios!:r(notes:t,store:t,version:t))>
@buffer_size<rs(AccessNumberActiveCodeMap:?r(_size!:N,map!:4=v(branch:r(left:!4,right:!4,size:N),empty:u,leaf:r(keyvals:8=?((11=r(hash:n32,key:t)r(code:t,generateTime:N))!8),size:N))),CanisterMap:?r(DTCTArchives:17=r(_size!:N,elements!:V?r(Actor:ra(append_DTCT:Fsr(vr(amount:i64,did:t,index:n64,summary:27=v(DSMSVerifyReward:t,ECIF:t,ICPCrowdfunding:n32,Mining:t,MiningReferralLevel1:t,MiningReferralLevel2:t,PreMint1DTCInEcosystem:n64,RecommendNewcomers:t,RegisterFromReferrer:t,ReleaseToMintDTC:n64),timestamp:n64))(b),append_USCT:Fsr(30=vr(adId:t,advertiser:t,amount:i64,checkTime:n64,created:n64,did:t,index:n64,summary:32=v(AdRewards:u,AdRewardsReferralLevel1:t,AdRewardsReferralLevel2:t,ConvertToUSDT:(fn64))))(b),cyclesBalance:35=Fqr()(n64),remaining_capacity:36=Fqr()(N)),fulled!:b,length!:N,skip:N),initCapacity:N),ICPfundsActor!:vra(computeExchangeAmount:Fqr(NN)(N),crowdfunding:Fsr(tNN)(NN),cyclesBalance:!36,deposit_cycles:41=Fsr(N)(b),get_transactions:Fqr(NN)(vr(DTCTqty:N,ICPqty:N,addressId:t,index:N,soldDTCTqty:N,timestamp:N)),rts_info:45=Fqr()(46=v47=(tN)),stateInfo:Fqr(N)(NNNN),trendPoints:Fqr(N)(v(NNN))),LedgerArchives:r(_size!:N,map!:53=v(branch:r(left:!53,right:!53,size:N),empty:u,leaf:r(keyvals:56=?((!1159=vt)!56),size:N))),Ledgers:r(_size!:N,map!:61=v(branch:r(left:!61,right:!61,size:N),empty:u,leaf:r(keyvals:64=?((!11t)!64),size:N))),USCTArchives:!17,accountCanisters:r(_size!:N,elements!:V?r(Actor:ra(_addHangupUSCT:Fsr(N73=r(adId:t,adInfo:t,advertiser:t,amount:n64,created:n64,did:t,summary:!32))(t),_appendReferee:Fs(tNtN)(),_childRewardTimeAndReferees2:Fqr(!59t76=v(level0:u,level1:u,level2:u)77=vN)(NN!59),_depositSafetyCode:78=Fs(tt)(),_getWalletBalance:Fqr(Ntt)(N),_setProvideSafety:Fsr(Ntt)(b),_updateWallet:Fs(NttN)(),_walletDefaultAddress:Fsr(N)(t),addConfirmedUSCT:Fs(!46)(),addendDSMSVerifyReward:Fs(tN85=?t)(),allowChangeMobile:86=Fsr(87=r(did:t,token:t)tt)(b),appTokenStatus:Fqr(N!87)(t),applyAndRenewDepositAddress:Fsr(ttt!87)(90=r(address:t,expiryTime:N,now:N)),applyDSMSVerifierIdentity:91=Fsr(tt!87)(b),changeMobileHistory:92=Fqr(!87)(!46),changeMobileNumber:!86,checkExist:Fqr(N)(b),childRewardTimeAndReferees:Fqr(!59t!76N)(NN!59),clearWalletExportError:!91,convertUSCT2USDT:Fsr(Nt!87)(b),createDappToken:Fsr(N!87)(r(accCanisterId:t,dAppIdentToken:!87,expiration:N)),createOne:Fsr(tttttN)(bNtt),crowdfundingICP:Fsr(Nt!87)(N),cyclesBalance:!36,dailyMining:100=Fsr(!87)(b),dailyMiningCount:FCr(!87)(102=r(award:N,countMining:N,validTime:N)!102!102),debugGetAccountInfo:Fqr(Nt)(?105=r(avatar:t,cCode:t,created:N,credits:r(DTCT:I,DTCTdecimals:n8,IncDTCT:N,IncUSCT:N,USCT:I,USCTdecimals:n8),index:N,miningDetail:r(clickTime:!77,days:N,status:110=v(inactive:u,mining:u)),mobile:t,nickname:t,referees:!59,referrer:t,roles:111=v(v(ambassador:u,core:u,developer:u,miner:u,verifier:u)N),safetyCirclesLength:N)),debugGetAccountMobile:114=Fqr(NNt)(115=v116=(Nt)),debugListMobile:!114,debugSwitchData:117=Fsr(ttt)(b),delSafetyCircle:Fsr(!87119=r(appellation:t,did:t))(b),delete:Fsr(!87t)(b),depositCallBack:Fs(Nttttt)(),deposit_cycles:!41,dsmsAccessStatus:Fqr(!87)(?124=(NN)),exportToken:125=Fsr(ttNtt!87)(N),exportTokenCallBack:Fsr(127=v(err:t,execution:u,ok:r(amount:N,chainCoinCode:t,chainFee:N,txHash:t),pending:t,rebuild:u)tttt)(b),forceLogin:Fsr(ttt)(tt),generate3virtualAddr:Fsr()(N),generateSafetyCode:Fsr(t)(132=?!119),generateSafetyCode2:Fsr(t!119)(!132),getAccountExtras:Fqr(!87t)(?v(ArrayNat:!77,ArrayText:!59,Blob:B,Int:I,Nat:N,Text:t)),getAccountInfo:138=Fqr(!87)(!105),getAccountInfo2:!138,getAccountStatus:Fqr(t)(v(delete:u,logIn:u,logOut:u,unregistered:u)),getDAppAcctInfo:Fqr(!87N)(r(avatar:t,cCode:t,nickname:t,roles:!111)),getDepositAddress:Fqr(...:Fsr(r(canister_id:P,settings:!210))()),index!:N,mcWalletActor!:vra(activateCoinLedger:Fsr(t)(t),bindDepositReceivWalletAddress:Fsr(ttt)(!160),bitcoin_balance:Fsr(251=v(Mainnet:u,Regtest:u,Testnet:u)t)(n64),buildTransaction:Fsr(tttNt)(b),callBackOfTask:Fsr(r(monitoredAddress:!59,receivedAmount:vr(chainIdent:t,coinCode:t,id:N,outsideAdderss:t,result:v(confirming:!171,received:!147),trxTime:N),sentTxRaw:v(Nt!127),updateOutFee:v(t262=v(baseBid:r(gasPrice:N),baseBidTip:r(gasPrice:N,maxPriorityFeePerGas:N),smartFee:r(estimateSmartFee:f)))))(r(receivedAmountResp:!115,sentTxRawResp:!115)),cyclesBalance:!36,deposit_cycles:!41,estimateNetWorkFee:Fqr(tt)(Nt),getBindAdderss:Fqr(t)(!85N),getChainInfos:Fqr()(270=vr(addressLength:t,addressPath:t,chainType:v(EVM:N,Null:u,TRON:N,UTXO:u),coinCode:t,confirmations:n8,explorer:t,ident:t,name:t,netFee:v(baseBid:u,baseBidTip:u,custom:u,fixed:N,smartFee:u),transactionPath:t)),getLedgerInitArgs:Fqr(t)(275=r(code:t,crossChainConfs:vv(Delta:u,contract:r(chainIdent:t,inputDecimals:N,standard:t,tokenAddress:t),native:r(chainIdent:t,inputDecimals:N)),decimals:N,name:t,symbol:t,transferFee:r(max:N,min:N,rate:N),web:t)),getOutFee:Fqr(t)(!262),get_current_fee_percentiles:Fsr(!251)(283=vn64),httpTransform:!235,listLedgerInitArgs:Fqr()(v!275),queryRSstatus:Fqr(tt!59)(r(received:vr(chainIdent:t,expiryTimeDiff:N,outsideAdderss:t,status:r(confirming:291=?!171,received:N)),sendTrx:vr(chainIdent:t,status:t,toAddr:t,txId:t))),queryTask:Fqr()(r(beMonitorAddress:!59,beSentTransactions:vr(bindKey:t,nonce:N,raw:t,sendAddr:t))),removeChainInfo:298=Fsr(t)(b),removeLedgerInitArg:!298,rts_info:!45,saveChainInfo:Fsr(!270)(b),saveLedgerInitArgs:Fsr(!275)(b),testQueryRawTransactionPool:Fqr()(vr(amount:N,bindKey:t,canisterId:t,chainCoinCode:t,nonce:N,raw:t,sendAddr:t,status:t,toAddr:t,txId:t)),testTransferNetWorkFee:Fqr(ttN)(r(In:N,out:!116)),testlistWalletMap:Fqr()(v(tr(balance:N,lastTxhash:t,lastsBind:vr(canisterId:t,confirming:!291,expiry:N,moneyAddress:t,received:N,startTime:N),lockAmount:N,publicKeyId:N))),transferOutBTC:Fsr(tN)(tN),transferOutICP:Fsr(!228N)(n64),unbindDeposit:Fs(ttt)()),mobileActor!:vra(changeDid2:Fsr(!171t)(b),changeMobile:!117,checkMob:Fqr(ttt)(!85),checkMob2:Fqr(N)(?!47),checkMobList:Fqr(!77)(!201),createOne:Fsr(tt)(b),createOne2:Fsr(Nt)(b),cyclesBalance:!36,deposit_cycles:!41,eachMap:Fqr()(t),getMob:Fqr()(!85),getNATMap:Fqr(N)(!85),initialize_usage_space:Fs()(),memory_size:!36,rts_info:!45,showStoreInfo:Fqr()(r(key_size:N,memory_used:N,nodeItems:N,pages:n64,pagesSpace:n64,usedStableMemory:t))),pendingUSCTActor!:vra(append:Fsr(!174)(b),confirm:Fsr(tn64!59!283)(!30),cyclesBalance:!35,sum_amount:Fqr(t)(n64),update_rts_info:Fsr()()),roadMapCanisterId!:t,statsActor!:vra(countByCountry:Fqr(t)(!46),countByDays:Fqr(NN)(v(Nvr(name:t,val:N))),cyclesBalance:!36,deposit_cycles:!41,getByCountry:Fqr(tt)(N),getTop100IncomeUSCT:!45,registerOne2:Fs(tN)(),rts_info:!45,update:Fs(v!124!46)()),totalCredit:r(consensusDev!:N,ecolog!:N,fuel!:N,max_supply:N,mining!:N)),CreditArchive_wasm_module:!209,Database:?r(accessNumbers:r(_size!:N,map!:352=v(branch:r(left:!352,right:!352,size:N),empty:u,leaf:r(keyvals:355=?((!11r(map:r(_size!:N,map!:360=v(branch:r(left:!360,right:!360,size:N),empty:u,leaf:r(keyvals:363=?((!11r(accessNumber!:t,carrier!:t,expiration!:N,online!:N,reqNum!:N,rewards!:N,succNum!:N))!363),size:N))),order:r(_size!:N,elements!:V!85,initCapacity:N)))!355),size:N))),verifyQueues:r(_size!:N,map!:370=v(branch:r(left:!370,right:!370,size:N),empty:u,leaf:r(keyvals:373=?((!11r(accessNumberData:vr(did:t,number:t,verifyStatus:v(partSuccs:u,success:u,waiting:u)),accountDid:!85,encryptKey:t,generateTime:N,verifyCode:t,verifyType:v(login:u,reg:u)))!373),size:N)))),LedgerArchive_wasm_module:!209,mobileApplastVersion:?r(android!:r(apk:t,notes:t,store:t,version:t),ios!:r(notes:t,store:t,version:t)))>
pre_exp
pre_upgrade
.
Consider gracefully handling failures from this canister or altering the canister to handle exceptions. See documentation: https://internetcomputer.org/docs/current/references/execution-errors#trapped, error code None

Maybe, @ggreif or @claudio could take a look?

Verify if variable mobileApplastVersion is definitely to be discarded. If so, declare it in the canister like so:

actor {
    ...
    stable let mobileApplastVersion : Any = ();
    ...
};

The compatibility check should pass without problems.

I’ll be monitoring here if you have more questions. Be aware that warnings from dfx deploy should not be taken lightly, you may lose data.

With recent moc versions you can discard stable variables using the (with migration) mechanism. Happy to give examples of that.

1 Like

By design, Motoko will not let you change the type of a stable variable to anything other than a supertype. This is to prevent data loss since the previous value must be able to initialize the stable variable at its new type, which only makes sense if the new type is the same or a supertype.

Without resorting the new migration function feature, if you want to ā€˜replace’ a stable variable you should promote the old one to have trivial type ā€˜Any’, and define a new variable with a different name and new, possibly unrelated, type.

With the new migration function feature, you can in fact replace the old variable with a new one with an unrelated type and keep the name.

This feature is very recent and only available with moc 0.14.0 and higher.

I’ll add doc links in a moment

1 Like

I am trying to repro your failure mode…
I presume that before you had something like this

actor {
  stable let mobileApplastVersion : {
    var android : {apk : Text; notes : Text; store : Text; version : Text};
    var ios : {notes : Text; store : Text; version : Text}
  } = {
    var android = {apk = ""; notes = ""; store = ""; version = ""};
    var ios = {notes = ""; store = ""; version = ""}
  }
}

and then you changed to this

actor {
  stable var mobileApplastVersion : {
    var android : {apk : Text; notes : Text; store : Text; version : Text};
    var ios : {notes : Text; store : Text; version : Text}
  } = {
    var android = {apk = ""; notes = ""; store = ""; version = ""};
    var ios = {notes = ""; store = ""; version = ""}
  }
}

So stable let → stable var. This would explain the message, but I cannot reproduce it in the Playground.

Something is missing. You may trigger an already fixed bug (I’ll go looking now)…

Which moc (or dfx) version are you using?

Older versions of Motoko would not let you change the mutability of stable variables.

Newer ones do allow this.

I believe the change was made with the same release that introduced the ā€˜ā€“enhanced-orthogonal-persistence’ feature.

I’m on mobile only so hard to say more.

1 Like

Thanks for the replies from all the experts.
However, the problem is still unresolved. The current version is dfx 0.24.0

The problem happened as follows: (The error code posted above is a prompt when trying to change to other methods after the problem)

Original version

actor {
  stable let mobileApplastVersion  {
    var android = {apk = ""; store = ""; version = ""};
    var ios = {store = ""; version = ""}
  }
}

Then change to the new version:

actor {
  stable let mobileApplastVersion  = {
    var android = {apk = ""; notes = ""; store = ""; version = ""};
    var ios = {notes = ""; store = ""; version = ""}
  }
}

When executing dfx canister install delta -m upgrade --ic, a prompt similar to stable let mobileApplastVersion : Any = () appeared, but I chose to continue. After completion, when I accessed the related functions that called the mobileApplastVersion variable, an error was reported. I could not upgrade by adjusting the code in this way.
I just changed the code to

actor { 
stable let mobileApplastVersion : Any = ();
}

But it still could not be upgraded completely, and the prompt was as follows:

dfx canister install delta -m upgrade --ic
Please enter the passphrase for your identity: [hidden]
Decryption complete.
Upgrading code for canister delta, with canister ID ojpsk-siaaa-aaaam-adtea-cai
WARNING!
Candid interface compatibility check failed for canister 'delta'.
You are making a BREAKING change. Other canisters or frontend clients relying on your canister may stop working.

Method getMobileApplastVersion is only in the expected type
Do you want to proceed? yes/No
yes
This canister requires an initialization argument.
Enter argument 1 of 2:
āœ” Enter a text (type :e to use editor) Ā· *********
Enter argument 2 of 2:
Enter a value for IcNetwork : variant { Mainnet; Local; Testnet }
āœ” Select a variant Ā· Mainnet
Sending the following argument:
("*********", variant { Mainnet })

Do you want to initialize the canister with this argument? [y/N]
y
Error: Failed to install wasm module to canister 'delta'.
Caused by: Failed during wasm installation call
Caused by: The replica returned a rejection error: reject code CanisterError, reject message Error from Canister ojpsk-siaaa-aaaam-adtea-cai: Canister trapped: heap out of bounds
Canister Backtrace:
obj_idx<0>
@buffer_size<r(notes:t,store:t,version:t)>
@buffer_size<Mr(notes:t,store:t,version:t)>
@buffer_size<r(android!:r(apk:t,notes:t,store:t,version:t),ios!:r(notes:t,store:t,version:t))>
@buffer_size<?r(android!:r(apk:t,notes:t,store:t,version:t),ios!:r(notes:t,store:t,version:t))>
@buffer_size<rs(AccessNumberActiveCodeMap:?r(_size!:N,map!:4=v(branch:r(left:!4,right:!4,size:N),empty:u,leaf:r(keyvals:8=?((11=r(hash:n32,key:t)r(code:t,generateTime:N))!8),size:N))),CanisterMap:?r(DTCTArchives:17=r(_size!:N,elements!:V?r(Actor:ra(append_DTCT:Fsr(vr(amount:i64,did:t,index:n64,summary:27=v(DSMSVerifyReward:t,ECIF:t,ICPCrowdfunding:n32,Mining:t,MiningReferralLevel1:t,MiningReferralLevel2:t,PreMint1DTCInEcosystem:n64,RecommendNewcomers:t,RegisterFromReferrer:t,ReleaseToMintDTC:n64),timestamp:n64))(b),append_USCT:Fsr(30=vr(adId:t,advertiser:t,amount:i64,checkTime:n64,created:n64,did:t,index:n64,summary:32=v(AdRewards:u,AdRewardsReferralLevel1:t,AdRewardsReferralLevel2:t,ConvertToUSDT:(fn64))))(b),cyclesBalance:35=Fqr()(n64),remaining_capacity:36=Fqr()(N)),fulled!:b,length!:N,skip:N),initCapacity:N),ICPfundsActor!:vra(computeExchangeAmount:Fqr(NN)(N),crowdfunding:Fsr(tNN)(NN),cyclesBalance:!36,deposit_cycles:41=Fsr(N)(b),get_transactions:Fqr(NN)(vr(DTCTqty:N,ICPqty:N,addressId:t,index:N,soldDTCTqty:N,timestamp:N)),rts_info:45=Fqr()(46=v47=(tN)),stateInfo:Fqr(N)(NNNN),trendPoints:Fqr(N)(v(NNN))),LedgerArchives:r(_size!:N,map!:53=v(branch:r(left:!53,right:!53,size:N),empty:u,leaf:r(keyvals:56=?((!1159=vt)!56),size:N))),Ledgers:r(_size!:N,map!:61=v(branch:r(left:!61,right:!61,size:N),empty:u,leaf:r(keyvals:64=?((!11t)!64),size:N))),USCTArchives:!17,accountCanisters:r(_size!:N,elements!:V?r(Actor:ra(_addHangupUSCT:Fsr(N73=r(adId:t,adInfo:t,advertiser:t,amount:n64,created:n64,did:t,summary:!32))(t),_appendReferee:Fs(tNtN)(),_childRewardTimeAndReferees2:Fqr(!59t76=v(level0:u,level1:u,level2:u)77=vN)(NN!59),_depositSafetyCode:78=Fs(tt)(),_getWalletBalance:Fqr(Ntt)(N),_setProvideSafety:Fsr(Ntt)(b),_updateWallet:Fs(NttN)(),_walletDefaultAddress:Fsr(N)(t),addConfirmedUSCT:Fs(!46)(),addendDSMSVerifyReward:Fs(tN85=?t)(),allowChangeMobile:86=Fsr(87=r(did:t,token:t)tt)(b),appTokenStatus:Fqr(N!87)(t),applyAndRenewDepositAddress:Fsr(ttt!87)(90=r(address:t,expiryTime:N,now:N)),applyDSMSVerifierIdentity:91=Fsr(tt!87)(b),changeMobileHistory:92=Fqr(!87)(!46),changeMobileNumber:!86,checkExist:Fqr(N)(b),childRewardTimeAndReferees:Fqr(!59t!76N)(NN!59),clearWalletExportError:!91,convertUSCT2USDT:Fsr(Nt!87)(b),createDappToken:Fsr(N!87)(r(accCanisterId:t,dAppIdentToken:!87,expiration:N)),createOne:Fsr(tttttN)(bNtt),crowdfundingICP:Fsr(Nt!87)(N),cyclesBalance:!36,dailyMining:100=Fsr(!87)(b),dailyMiningCount:FCr(!87)(102=r(award:N,countMining:N,validTime:N)!102!102),debugGetAccountInfo:Fqr(Nt)(?105=r(avatar:t,cCode:t,created:N,credits:r(DTCT:I,DTCTdecimals:n8,IncDTCT:N,IncUSCT:N,USCT:I,USCTdecimals:n8),index:N,miningDetail:r(clickTime:!77,days:N,status:110=v(inactive:u,mining:u)),mobile:t,nickname:t,referees:!59,referrer:t,roles:111=v(v(ambassador:u,core:u,developer:u,miner:u,verifier:u)N),safetyCirclesLength:N)),debugGetAccountMobile:114=Fqr(NNt)(115=v116=(Nt)),debugListMobile:!114,debugSwitchData:117=Fsr(ttt)(b),delSafetyCircle:Fsr(!87119=r(appellation:t,did:t))(b),delete:Fsr(!87t)(b),depositCallBack:Fs(Nttttt)(),deposit_cycles:!41,dsmsAccessStatus:Fqr(!87)(?124=(NN)),exportToken:125=Fsr(ttNtt!87)(N),exportTokenCallBack:Fsr(127=v(err:t,execution:u,ok:r(amount:N,chainCoinCode:t,chainFee:N,txHash:t),pending:t,rebuild:u)tttt)(b),forceLogin:Fsr(ttt)(tt),generate3virtualAddr:Fsr()(N),generateSafetyCode:Fsr(t)(132=?!119),generateSafetyCode2:Fsr(t!119)(!132),getAccountExtras:Fqr(!87t)(?v(ArrayNat:!77,ArrayText:!59,Blob:B,Int:I,Nat:N,Text:t)),getAccountInfo:138=Fqr(!87)(!105),getAccountInfo2:!138,getAccountStatus:Fqr(t)(v(delete:u,logIn:u,logOut:u,unregistered:u)),getDAppAcctInfo:Fqr(!87N)(r(avatar:t,cCode:t,nickname:t,roles:!111)),getDepositAddress:Fqr(...:Fsr(r(canister_id:P,settings:!210))()),index!:N,mcWalletActor!:vra(activateCoinLedger:Fsr(t)(t),bindDepositReceivWalletAddress:Fsr(ttt)(!160),bitcoin_balance:Fsr(251=v(Mainnet:u,Regtest:u,Testnet:u)t)(n64),buildTransaction:Fsr(tttNt)(b),callBackOfTask:Fsr(r(monitoredAddress:!59,receivedAmount:vr(chainIdent:t,coinCode:t,id:N,outsideAdderss:t,result:v(confirming:!171,received:!147),trxTime:N),sentTxRaw:v(Nt!127),updateOutFee:v(t262=v(baseBid:r(gasPrice:N),baseBidTip:r(gasPrice:N,maxPriorityFeePerGas:N),smartFee:r(estimateSmartFee:f)))))(r(receivedAmountResp:!115,sentTxRawResp:!115)),cyclesBalance:!36,deposit_cycles:!41,estimateNetWorkFee:Fqr(tt)(Nt),getBindAdderss:Fqr(t)(!85N),getChainInfos:Fqr()(270=vr(addressLength:t,addressPath:t,chainType:v(EVM:N,Null:u,TRON:N,UTXO:u),coinCode:t,confirmations:n8,explorer:t,ident:t,name:t,netFee:v(baseBid:u,baseBidTip:u,custom:u,fixed:N,smartFee:u),transactionPath:t)),getLedgerInitArgs:Fqr(t)(275=r(code:t,crossChainConfs:vv(Delta:u,contract:r(chainIdent:t,inputDecimals:N,standard:t,tokenAddress:t),native:r(chainIdent:t,inputDecimals:N)),decimals:N,name:t,symbol:t,transferFee:r(max:N,min:N,rate:N),web:t)),getOutFee:Fqr(t)(!262),get_current_fee_percentiles:Fsr(!251)(283=vn64),httpTransform:!235,listLedgerInitArgs:Fqr()(v!275),queryRSstatus:Fqr(tt!59)(r(received:vr(chainIdent:t,expiryTimeDiff:N,outsideAdderss:t,status:r(confirming:291=?!171,received:N)),sendTrx:vr(chainIdent:t,status:t,toAddr:t,txId:t))),queryTask:Fqr()(r(beMonitorAddress:!59,beSentTransactions:vr(bindKey:t,nonce:N,raw:t,sendAddr:t))),removeChainInfo:298=Fsr(t)(b),removeLedgerInitArg:!298,rts_info:!45,saveChainInfo:Fsr(!270)(b),saveLedgerInitArgs:Fsr(!275)(b),testQueryRawTransactionPool:Fqr()(vr(amount:N,bindKey:t,canisterId:t,chainCoinCode:t,nonce:N,raw:t,sendAddr:t,status:t,toAddr:t,txId:t)),testTransferNetWorkFee:Fqr(ttN)(r(In:N,out:!116)),testlistWalletMap:Fqr()(v(tr(balance:N,lastTxhash:t,lastsBind:vr(canisterId:t,confirming:!291,expiry:N,moneyAddress:t,received:N,startTime:N),lockAmount:N,publicKeyId:N))),transferOutBTC:Fsr(tN)(tN),transferOutICP:Fsr(!228N)(n64),unbindDeposit:Fs(ttt)()),mobileActor!:vra(changeDid2:Fsr(!171t)(b),changeMobile:!117,checkMob:Fqr(ttt)(!85),checkMob2:Fqr(N)(?!47),checkMobList:Fqr(!77)(!201),createOne:Fsr(tt)(b),createOne2:Fsr(Nt)(b),cyclesBalance:!36,deposit_cycles:!41,eachMap:Fqr()(t),getMob:Fqr()(!85),getNATMap:Fqr(N)(!85),initialize_usage_space:Fs()(),memory_size:!36,rts_info:!45,showStoreInfo:Fqr()(r(key_size:N,memory_used:N,nodeItems:N,pages:n64,pagesSpace:n64,usedStableMemory:t))),pendingUSCTActor!:vra(append:Fsr(!174)(b),confirm:Fsr(tn64!59!283)(!30),cyclesBalance:!35,sum_amount:Fqr(t)(n64),update_rts_info:Fsr()()),roadMapCanisterId!:t,statsActor!:vra(countByCountry:Fqr(t)(!46),countByDays:Fqr(NN)(v(Nvr(name:t,val:N))),cyclesBalance:!36,deposit_cycles:!41,getByCountry:Fqr(tt)(N),getTop100IncomeUSCT:!45,registerOne2:Fs(tN)(),rts_info:!45,update:Fs(v!124!46)()),totalCredit:r(consensusDev!:N,ecolog!:N,fuel!:N,max_supply:N,mining!:N)),CreditArchive_wasm_module:!209,Database:?r(accessNumbers:r(_size!:N,map!:352=v(branch:r(left:!352,right:!352,size:N),empty:u,leaf:r(keyvals:355=?((!11r(map:r(_size!:N,map!:360=v(branch:r(left:!360,right:!360,size:N),empty:u,leaf:r(keyvals:363=?((!11r(accessNumber!:t,carrier!:t,expiration!:N,online!:N,reqNum!:N,rewards!:N,succNum!:N))!363),size:N))),order:r(_size!:N,elements!:V!85,initCapacity:N)))!355),size:N))),verifyQueues:r(_size!:N,map!:370=v(branch:r(left:!370,right:!370,size:N),empty:u,leaf:r(keyvals:373=?((!11r(accessNumberData:vr(did:t,number:t,verifyStatus:v(partSuccs:u,success:u,waiting:u)),accountDid:!85,encryptKey:t,generateTime:N,verifyCode:t,verifyType:v(login:u,reg:u)))!373),size:N)))),LedgerArchive_wasm_module:!209,mobileApplastVersion:?r(android!:r(apk:t,notes:t,store:t,version:t),ios!:r(notes:t,store:t,version:t)))>
pre_exp
pre_upgrade
.
Consider gracefully handling failures from this canister or altering the canister to handle exceptions. See documentation: https://internetcomputer.org/docs/current/references/execution-errors#trapped, error code None
1 Like

Looking forward to help, thanks!

1 Like

Looking forward to help thanks!

1 Like

Your upgrade is adding a new nested record field ā€˜note’, which is not allowed because the new type is not a supertype of the old type.

The upgrade would need to fabricate some arbitrary value for this field.

This link describes a workaround.

There’s a better solution using an explicit migration function but our documentation seems to have reverted to and out of date version and does not explain it.

I can fix the documentation on Monday when back at work (on holiday now with mobile only)

1 Like

Due to this issue, we are unable to continue our work. We look forward to seeing you back and wish you a happy weekend!

We are all for you continuing your work, so here is a possible recipe for resolving your problem:

  • revert mobileApplastVersion to the last correct type; I think it was:
    stable let mobileApplastVersion  {
      var android = {apk = ""; store = ""; version = ""};
      var ios = {store = ""; version = ""}
    }
    
  • declare a second variable
    stable var mobileApplastVersion_2  = {
      android = {apk = ""; notes = ""; store = ""; version = ""};
      ios = {notes = ""; store = ""; version = ""}
    }
    
  • in the upgrade hook system func postupgrade() { ... } insert this line
    ...
    mobileApplastVersion_2 := {
      android = { mobileApplastVersion.android with notes = "" };
      ios = { mobileApplastVersion.ios with notes = "" };
    }
    ...
    
  • change all reading references to mobileApplastVersion.android to mobileApplastVersion_2.android
  • similarly for ios
  • change all writing references to mobileApplastVersion.android to
    mobileApplastVersion_2 := { mobileApplastVersion_2 with android = <new-value> };
    
  • similarly for ios
  • make sure that mobileApplastVersion is never accessed (other than in the postupgrade hook)

Note: This way there is no need to have var fields in the mobileApplastVersion_2 which are problematic from the subtyping perspective. But this is just a suggestion, you can keep it if you want but then you have to do mobileApplastVersion_2.android := <new-value> and write var android for mutable field construction.

This is assuming you want to stick to dfx 0.24. If you are ready to migrate to 0.25.x we’ll be happy to update this recipe. It would make eliminating previous stable bindings easy and also changing types unrestricted.

PS.: Please read up on { <base> with field = <val>; ... } functional record extension syntax, it’ll make your life much easier for the future.

Thanks for your reply!

Maybe you didn’t understand what I meant (I have described the whole process in detail above).
I have followed your instructions, but it still doesn’t work!




We no longer need to restore the data of the mobileApplastVersion variable. We can use any method to update the code and keep valid data.
Original version

actor {
  stable let mobileApplastVersion  {
    var android = {apk = ""; store = ""; version = ""};
    var ios = {store = ""; version = ""}
  }
}

Then change to the new version:

actor {
  stable let mobileApplastVersion  = {
    var android = {apk = ""; notes = ""; store = ""; version = ""};
    var ios = {notes = ""; store = ""; version = ""}
  }
}

Let me describe the process in another way:
The first time we made this change, the program was successfully updated, but when prompted with the risk, we selected ā€œyesā€ and performed a forced update. After that, 1. When accessing the variable mobileApplastVersion through the function, it prompted Error from Canister ojpsk-siaaa-aaaam-adtea-cai: Canister trapped: heap out of bounds. 2. No matter how we adjusted the code later, it could not be upgraded and prompted ...Canister trapped: heap out of bounds, which was also prompted for the upgrade.

Is the canister still responsive or do you get heap out bounds for every method call?

Do you know how much memory the canister is using?

Also, does your actor have an explicit ā€˜preupgrade’ system method? I wonder if that might be running out of memory trying to explicitly save some data.

The stacktrace suggests that a preupgrade method might be failing, but I might be wrong.

Ok, I have an idea about what may have happened and how you may be able to fix it (no guarantees, I’m afraid).

First, try to take a snapshot of the current canister so you can revert if things go wrong after the following steps.
See: Canister snapshots | Internet Computer

Now for the fix:

What I think may have happened is that the original upgrade (where you ignored the warning but shouldn’t have) ā€œsucceededā€ but somehow corrupted the value in mobileApplastVersion due to the incompatible types of this field.

I can see your canister here:

https://dashboard.internetcomputer.org/canister/ojpsk-siaaa-aaaam-adtea-cai

Most query methods seem to work fine apart from:

https://dashboard.internetcomputer.org/canister/ojpsk-siaaa-aaaam-adtea-cai#getMobileApplastVersion

That method presumably returns the contents of a field of mobileApplastVersion but currently fails with ā€œCanister trapped: heap out of boundsā€
I suspect the upgrade corrupted the value stored in the field so the field cannot be serialized as a Candid value, both in the implementation of this method but also when trying to upgrade the canister.

The possible fix is to try and reset the value of the field(s) using other methods in your canister.

I noticed that your canister exposes a method for (presumably) updating the andriod/ios values:

 updateMobileApplastVersion: (vec MobileApplastVersion) -> (bool);

If that method doesn’t use the current value of the fields, but just writes to them, then you may be able to manually reset the stable variable with non-corrupted values.

One you’ve called that method with both an android and an ios argument, to restore both variables, you could try to call getMobileApplastVersion to see if it now works correctly.

If calling getMobileApplastVersion now works, I expect your next upgrade will also work, instead of failing when trying to save the corrupt value in mobileApplastVersion.

Like I said, there is no guarantee this fix will work, but provided you take a snapshot first, I think it may be worth a try.

(I don’t think your canister is even close to being out of memory, since
https://dashboard.internetcomputer.org/canister/ojpsk-siaaa-aaaam-adtea-cai#rts_info
reports at most 20.6 MB of memory use and 6.3 MB of heap)

(
  vec { record { "memory_size"; 20_643_840 : nat;}; 
           record { "heap_size"; 6_252_456 : nat;}; 
           record { "total_allocation"; 706_265_328 : nat;}; 
           record { "reclaimed"; 700_012_856 : nat;}; 
           record { "max_live_size"; 1_945_264 : nat;}; 
           record { "callback_table_count"; 0 : nat;};
           record { "callback_table_size"; 256 : nat;}; 
           record { "mutator_instructions"; 10_938 : nat;};
           record { "collector_instructions"; 301 : nat;}; 
           record { "logical_stable_memory_size"; 0 : nat;}; 
           record { "max_stack_size"; 2_097_152 : nat;};
           record { "stable_memory_size"; 1_900_544 : nat;}; 
           record { "cyclesBalance"; 8_233_291_997_060 : nat;};},
)

UPDATE:

I tried this recipe on a toy canister and it appears to fix the problem:
See here:

Of course, that’s not a guarantee it will work for you.

1 Like

Great! I have restored the program according to your suggestion. You saved me. You are my God. Thank you!

At the same time, I am willing to upgrade to dfx 0.25.x
To upgrade the online dfx version, do I just need to use 0.25.x to complete the build locally and then execute dfx canister install delta -m upgrade --ic

Is it also applicable to the internal sub-container to upload wasm and then execute IC.install_code({... mode = #upgrade;})?

Or do I need other methods?

2 Likes

I’m glad it worked.

Your upgrade strategy sounds ok to me.

The separate wasm will also need to be
recompiled if you want to upgrade that.

However, please be aware that the static guard against incompatible upgrades is only applied by upgrades using ā€˜dfx’ not (in general) with IC.install_code, so be very careful if you are changing stable variables in the wasm installed with IC.install_code

Whatever you do, please do not ignore the dfx warning about incompatible stable variables. Next time, it might not be so easy to fix.

1 Like