Compatibility error

I get the following error:

(unknown location): Compatibility error [M0170], stable variable _stableChunks of previous type
  var [var StableChunk__6]
cannot be consumed at new type
  var [var StableChunk__6__1]

Do you want to proceed? yes/No

StableChunk__6__1 does neither exist in old.most nor in most, why is this error thrown @claudio ?

production.old.most:

// Version: 1.0.0
type AccountIdentifier = Text;
type AccountIdentifier__1 = AccountIdentifier;
type AccountIdentifier__2 = AccountIdentifier__1;
type AccountIdentifier__4 = AccountIdentifier__1;
type AccountIdentifier__6 = AccountIdentifier__1;
type AccountIdentifier__8 = AccountIdentifier__1;
type Asset =
  {metadata : ?File; name : Text; payload : File; thumbnail : ?File};
type Balance = Nat;
type Balance__1 = Balance;
type DayCanisterCyclesData = [var Nat64];
type DayCanisterHeapMemorySizeData = [var Nat64];
type DayCanisterMemorySizeData = [var Nat64];
type DayData =
  {
    canisterCyclesData : DayCanisterCyclesData;
    canisterHeapMemorySizeData : DayCanisterHeapMemorySizeData;
    canisterMemorySizeData : DayCanisterMemorySizeData;
    updateCallsData : DayUpdateCallsCountData
  };
type DayDataId = Nat32;
type DayUpdateCallsCountData = [var Nat64];
type Disbursement =
  {
    amount : Nat64;
    fromSubaccount : SubAccount__4;
    to : AccountIdentifier__4;
    tokenIndex : TokenIndex__2
  };
type File = {ctype : Text; data : [Blob]};
type Frontend = {accountIdentifier : AccountIdentifier__8; fee : Nat64};
type Listing__1 =
  {
    buyerFrontend : ?Text;
    locked : ?Time__3;
    price : Nat64;
    seller : Principal;
    sellerFrontend : ?Text
  };
type Metadata =
  {
    #fungible :
      {decimals : Nat8; metadata : ?Blob; name : Text; symbol : Text};
    #nonfungible : {metadata : ?Blob}
  };
type Metadata__1 = Metadata;
type Sale =
  {
    buyer : AccountIdentifier__6;
    expires : Time__2;
    price : Nat64;
    slot : ?WhitelistSlot__1;
    subaccount : SubAccount__6;
    tokens : [TokenIndex__4]
  };
type SaleTransaction =
  {
    buyer : AccountIdentifier__6;
    price : Nat64;
    seller : Principal;
    time : Time__2;
    tokens : [TokenIndex__4]
  };
type Settlement =
  {
    buyer : AccountIdentifier__8;
    buyerFrontend : ?Text;
    price : Nat64;
    seller : Principal;
    sellerFrontend : ?Text;
    subaccount : SubAccount__8
  };
type StableChunk =
  ?{
     #legacy : StableState;
     #v1 :
       {
         nextTokenId : TokenIndex__1;
         owners : [(AccountIdentifier__2, [TokenIndex__1])];
         registry : [(TokenIndex__1, AccountIdentifier__2)];
         supply : Balance__1;
         tokenMetadata : [(TokenIndex__1, Metadata__1)]
       }
   };
type StableChunk__1 =
  ?{
     #legacy : StableState__1;
     #v1 : {assetsChunk : [Asset]; assetsCount : Nat};
     #v1_chunk : {assetsChunk : [Asset]}
   };
type StableChunk__2 =
  ?{#legacy : StableState__2; #v1 : {disbursements : [Disbursement]}};
type StableChunk__3 = ?{#legacy : StableState__3; #v1 : {isShuffled : Bool}};
type StableChunk__4 =
  ?{
     #legacy : StableState__4;
     #v1 :
       {
         failedSales : [(AccountIdentifier__6, SubAccount__6)];
         nextSubAccount : Nat;
         saleTransactionChunk : [SaleTransaction];
         saleTransactionCount : Nat;
         salesSettlements : [(AccountIdentifier__6, Sale)];
         sold : Nat;
         soldIcp : Nat64;
         tokensForSale : [TokenIndex__4];
         totalToSell : Nat;
         whitelist : [(Nat64, AccountIdentifier__6, WhitelistSlot__1)]
       };
     #v1_chunk : {saleTransactionChunk : [SaleTransaction]}
   };
type StableChunk__5 =
  ?{
     #legacy : StableState__5;
     #v1 :
       {
         frontends : [(Text, Frontend)];
         tokenListing : [(TokenIndex__5, Listing__1)];
         tokenSettlement : [(TokenIndex__5, Settlement)];
         transactionChunk : [Transaction__2];
         transactionCount : Nat
       };
     #v1_chunk : {transactionChunk : [Transaction__2]}
   };
type StableChunk__6 =
  {
    #v1 :
      {
        assets : StableChunk__1;
        disburser : StableChunk__2;
        marketplace : StableChunk__5;
        sale : StableChunk__4;
        shuffle : StableChunk__3;
        tokens : StableChunk
      }
  };
type StableState =
  {
    _nextTokenIdState : TokenIndex__1;
    _ownersState : [(AccountIdentifier__2, [TokenIndex__1])];
    _registryState : [(TokenIndex__1, AccountIdentifier__2)];
    _supplyState : Balance__1;
    _tokenMetadataState : [(TokenIndex__1, Metadata__1)]
  };
type StableState__1 = {_assetsState : [Asset]};
type StableState__2 = {_disbursementsState : [Disbursement]};
type StableState__3 = {_isShuffledState : Bool};
type StableState__4 =
  {
    _failedSalesState : [(AccountIdentifier__6, SubAccount__6)];
    _nextSubAccountState : Nat;
    _saleTransactionsState : [SaleTransaction];
    _salesSettlementsState : [(AccountIdentifier__6, Sale)];
    _soldIcpState : Nat64;
    _soldState : Nat;
    _tokensForSaleState : [TokenIndex__4];
    _totalToSellState : Nat;
    _whitelistStable : [(Nat64, AccountIdentifier__6, WhitelistSlot__1)]
  };
type StableState__5 =
  {
    _frontendsState : [(Text, Frontend)];
    _tokenListingState : [(TokenIndex__5, Listing__1)];
    _tokenSettlementState : [(TokenIndex__5, Settlement)];
    _transactionsState : [Transaction__2]
  };
type SubAccount = [Nat8];
type SubAccount__1 = SubAccount;
type SubAccount__4 = SubAccount__1;
type SubAccount__6 = SubAccount__1;
type SubAccount__8 = SubAccount__1;
type Time = Int;
type Time__2 = Time;
type Time__3 = Time;
type TokenIdentifier = Text;
type TokenIdentifier__1 = TokenIdentifier;
type TokenIdentifier__3 = TokenIdentifier__1;
type TokenIndex = Nat32;
type TokenIndex__1 = TokenIndex;
type TokenIndex__2 = TokenIndex;
type TokenIndex__4 = TokenIndex;
type TokenIndex__5 = TokenIndex;
type Transaction__2 =
  {
    buyer : AccountIdentifier__8;
    price : Nat64;
    seller : Principal;
    time : Time__3;
    token : TokenIdentifier__3
  };
type UpgradeDataDayTuple = (DayDataId, DayData);
type UpgradeData__1 = {#v1 : {dayData : [UpgradeDataDayTuple]}};
type UpgradeData__2 = UpgradeData__1;
type WhitelistSlot = {end : Time; start : Time};
type WhitelistSlot__1 = WhitelistSlot;
actor {
  stable var _assetsState : {_assetsState : [Asset]};
  stable var _canistergeekMonitorUD : ?UpgradeData__2;
  stable var _disburserState : {_disbursementsState : [Disbursement]};
  stable var _marketplaceState :
    {
      _frontendsState : [(Text, Frontend)];
      _tokenListingState : [(TokenIndex__5, Listing__1)];
      _tokenSettlementState : [(TokenIndex__5, Settlement)];
      _transactionsState : [Transaction__2]
    };
  stable var _revealTimerId : Nat;
  stable var _saleState :
    {
      _failedSalesState : [(AccountIdentifier__6, SubAccount__6)];
      _nextSubAccountState : Nat;
      _saleTransactionsState : [SaleTransaction];
      _salesSettlementsState : [(AccountIdentifier__6, Sale)];
      _soldIcpState : Nat64;
      _soldState : Nat;
      _tokensForSaleState : [TokenIndex__4];
      _totalToSellState : Nat;
      _whitelistStable : [(Nat64, AccountIdentifier__6, WhitelistSlot__1)]
    };
  stable var _shuffleState : {_isShuffledState : Bool};
  stable var _stableChunks : [var StableChunk__6];
  stable var _timerId : Nat;
  stable var _tokenState :
    {
      _nextTokenIdState : TokenIndex__1;
      _ownersState : [(AccountIdentifier__2, [TokenIndex__1])];
      _registryState : [(TokenIndex__1, AccountIdentifier__2)];
      _supplyState : Balance__1;
      _tokenMetadataState : [(TokenIndex__1, Metadata__1)]
    };
  stable var rootBucketId : ?Text
};

production.most

// Version: 1.0.0
type AccountIdentifier = Text;
type AccountIdentifier__1 = AccountIdentifier;
type AccountIdentifier__2 = AccountIdentifier__1;
type AccountIdentifier__4 = AccountIdentifier__1;
type AccountIdentifier__6 = AccountIdentifier__1;
type AccountIdentifier__8 = AccountIdentifier__1;
type Asset =
  {metadata : ?File; name : Text; payload : File; thumbnail : ?File};
type Balance = Nat;
type Balance__1 = Balance;
type DayCanisterCyclesData = [var Nat64];
type DayCanisterHeapMemorySizeData = [var Nat64];
type DayCanisterMemorySizeData = [var Nat64];
type DayData =
  {
    canisterCyclesData : DayCanisterCyclesData;
    canisterHeapMemorySizeData : DayCanisterHeapMemorySizeData;
    canisterMemorySizeData : DayCanisterMemorySizeData;
    updateCallsData : DayUpdateCallsCountData
  };
type DayDataId = Nat32;
type DayUpdateCallsCountData = [var Nat64];
type Disbursement =
  {
    amount : Nat64;
    fromSubaccount : SubAccount__4;
    to : AccountIdentifier__4;
    tokenIndex : TokenIndex__2
  };
type File = {ctype : Text; data : [Blob]};
type Frontend = {accountIdentifier : AccountIdentifier__8; fee : Nat64};
type Listing__1 =
  {
    buyerFrontend : ?Text;
    locked : ?Time__3;
    price : Nat64;
    seller : Principal;
    sellerFrontend : ?Text
  };
type Metadata =
  {
    #fungible :
      {decimals : Nat8; metadata : ?Blob; name : Text; symbol : Text};
    #nonfungible : {metadata : ?Blob}
  };
type Metadata__1 = Metadata;
type Sale =
  {
    buyer : AccountIdentifier__6;
    expires : Time__2;
    price : Nat64;
    slot : ?WhitelistSlot__1;
    subaccount : SubAccount__6;
    tokens : [TokenIndex__4]
  };
type SaleTransaction =
  {
    buyer : AccountIdentifier__6;
    price : Nat64;
    seller : Principal;
    time : Time__2;
    tokens : [TokenIndex__4]
  };
type Settlement =
  {
    buyer : AccountIdentifier__8;
    buyerFrontend : ?Text;
    price : Nat64;
    seller : Principal;
    sellerFrontend : ?Text;
    subaccount : SubAccount__8
  };
type StableChunk =
  ?{
     #v1 :
       {
         nextTokenId : TokenIndex__1;
         owners : [(AccountIdentifier__2, [TokenIndex__1])];
         registry : [(TokenIndex__1, AccountIdentifier__2)];
         supply : Balance__1;
         tokenMetadata : [(TokenIndex__1, Metadata__1)]
       }
   };
type StableChunk__1 =
  ?{
     #v1 : {assetsChunk : [Asset]; assetsCount : Nat};
     #v1_chunk : {assetsChunk : [Asset]}
   };
type StableChunk__2 = ?{#v1 : {disbursements : [Disbursement]}};
type StableChunk__3 = ?{#v1 : {isShuffled : Bool}};
type StableChunk__4 =
  ?{
     #v1 :
       {
         failedSales : [(AccountIdentifier__6, SubAccount__6)];
         nextSubAccount : Nat;
         saleTransactionChunk : [SaleTransaction];
         saleTransactionCount : Nat;
         salesSettlements : [(AccountIdentifier__6, Sale)];
         sold : Nat;
         soldIcp : Nat64;
         tokensForSale : [TokenIndex__4];
         totalToSell : Nat;
         whitelist : [(Nat64, AccountIdentifier__6, WhitelistSlot__1)]
       };
     #v1_chunk : {saleTransactionChunk : [SaleTransaction]}
   };
type StableChunk__5 =
  ?{
     #v1 :
       {
         frontends : [(Text, Frontend)];
         tokenListing : [(TokenIndex__5, Listing__1)];
         tokenSettlement : [(TokenIndex__5, Settlement)];
         transactionChunk : [Transaction__2];
         transactionCount : Nat
       };
     #v1_chunk : {transactionChunk : [Transaction__2]}
   };
type StableChunk__6 =
  {
    #v1 :
      {
        assets : StableChunk__1;
        disburser : StableChunk__2;
        marketplace : StableChunk__5;
        sale : StableChunk__4;
        shuffle : StableChunk__3;
        tokens : StableChunk
      }
  };
type SubAccount = [Nat8];
type SubAccount__1 = SubAccount;
type SubAccount__4 = SubAccount__1;
type SubAccount__6 = SubAccount__1;
type SubAccount__8 = SubAccount__1;
type Time = Int;
type Time__2 = Time;
type Time__3 = Time;
type TokenIdentifier = Text;
type TokenIdentifier__1 = TokenIdentifier;
type TokenIdentifier__3 = TokenIdentifier__1;
type TokenIndex = Nat32;
type TokenIndex__1 = TokenIndex;
type TokenIndex__2 = TokenIndex;
type TokenIndex__4 = TokenIndex;
type TokenIndex__5 = TokenIndex;
type Transaction__2 =
  {
    buyer : AccountIdentifier__8;
    price : Nat64;
    seller : Principal;
    time : Time__3;
    token : TokenIdentifier__3
  };
type UpgradeDataDayTuple = (DayDataId, DayData);
type UpgradeData__1 = {#v1 : {dayData : [UpgradeDataDayTuple]}};
type UpgradeData__2 = UpgradeData__1;
type WhitelistSlot = {end : Time; start : Time};
type WhitelistSlot__1 = WhitelistSlot;
actor {
  stable var _assetsState : Any;
  stable var _canistergeekMonitorUD : ?UpgradeData__2;
  stable var _disburserState : Any;
  stable var _marketplaceState : Any;
  stable var _revealTimerId : Nat;
  stable var _saleState : Any;
  stable var _shuffleState : Any;
  stable var _stableChunks : [var StableChunk__6];
  stable var _timerId : Nat;
  stable var _tokenState : Any;
  stable var rootBucketId : ?Text
};

My hunch is this is because the arrays are mutable so the type inside cannot be changed at all.
Did any of the types reference from the mutable array element type change?

The type of a stable variable can only evolve to a supertype (so that the upgrade can consume the old value when initializing the field at the new type). Unfortunately, the subtyping relation between mutable arrays requires the element type to be equivalent, not just a subtype (as for immutable arrays). This is actually required for soundness, in general.

Didn’t the subtype check for stable upgrades ignore mutability? It shouldn’t matter there, right?

I think it’s errored due to the removal of #legacy : StableState;. If you are sure there is no #legacy data you can upgrade w/o data loss.

Also there is error when you add new variant, for example #v2, should not it be safe to upgrade in that case?

It would also be great to see all compatibility check errors.
For example, if I am sure there is no #legacy data and remove it, but also if I change inner structure in #v1 it will show the same error, so there is a chance that i will miss it and lose data.

1 Like

I think it does matter.

If you have two stable variables that reference the same mutable array, they cannot safely upgrade to different types. Writing a value of the new type to one may be observed by reads from the other.

You can migrate a top-level stable var to a supertype, but it still has to be a supertype and mutable arrays are invariant so the only legal supertype is ‘Any’.

Note here that the content type of the mutable stable variable is itself mutable.

Ah yes, good point about possible sharing.

Yes, they did change. E.g. from

type StableChunk__1 =
  ?{
     #legacy : StableState__1;
     #v1 : {assetsChunk : [Asset]; assetsCount : Nat};
     #v1_chunk : {assetsChunk : [Asset]}
   };

to

type StableChunk__1 =
  ?{
     #v1 : {assetsChunk : [Asset]; assetsCount : Nat};
     #v1_chunk : {assetsChunk : [Asset]}
   };

What would be the best way to structure our data so that we can add new variants to StableChunk__1 ?

And +1 to what @ZenVoich asked