Ic-stable-structures Panicked at 'Attempting to allocate an already allocated chunk

  • I found it it is size issue, I just delete some data and tried to store something and it was success.

  • Also two of my static have the same MemoryId::new(5) is it safe to update just one of them? or I could lose my data.

  • I tried to save short content data to my backend and it was fine. But when the content get bigger i can’t save anymore

  • Could that happened because I migrate some field before like adding new field or removing field in the struct ?

  • I think that because locally i ran dfx deploy backend --mode=reinstall and the error gone. But on the IC i don’t wanna lose my data.

version

ic-stable-structures = "0.6.7"

my code

pub struct ContentNode {
    pub formats: Vec<String>,  // <---------------- new field 
    pub id: ContentId,
    pub parent: Option<ContentId>,
    pub _type: String,
    pub value: String,
    pub text: String,
    pub language: String,
    pub indent: u64,
    pub data: Option<ContentData>,
    pub listStyleType: String,
    pub listStart: u64,
    #[serde(default)]
    pub children: Vec<ContentId>,
}


let current_user = caller().to_string();
        FILE_CONTENTS.with(|file_contents| {
            let mut contents = file_contents.borrow_mut();
            let mut user_contents = contents.get(&current_user).unwrap_or_else(|| {
                ContentNodeVec {
                    files: HashMap::new(),
                }
            });
            user_contents.files.insert(file_id, content_nodes);
            contents.insert(current_user, user_contents);
        });

impl Storable for ContentNodeVec {
    fn to_bytes(&self) -> Cow<[u8]> {
        Cow::Owned(Encode!(self).unwrap())
    }

    fn from_bytes(bytes: Cow<[u8]>) -> Self {
        // First try to decode as the new format
        match Decode!(bytes.as_ref(), Self) {
            Ok(content) => content,
            Err(_) => {
                // If that fails, try to decode as old format and migrate
                let old_content: HashMap<ContentId, OldContentNode> = 
                    Decode!(bytes.as_ref(), HashMap<ContentId, OldContentNode>)
                        .unwrap_or_default();
                
                // Migrate old format to new format
                let contents = old_content
                    .into_iter()
                    .map(|(id, old_node)| {
                        let new_node = ContentNode {
                            formats: Vec::new(), // Initialize with empty formats
                            id: old_node.id,
                            parent: old_node.parent,
                            _type: old_node._type,
                            value: old_node.value,
                            text: old_node.text,
                            language: old_node.language,
                            indent: old_node.indent,
                            data: old_node.data,
                            listStyleType: old_node.listStyleType,
                            listStart: old_node.listStart,
                            children: old_node.children,
                        };
                        (id, new_node)
                    })
                    .collect();

                ContentNodeVec { contents }
            }
        }
    }

    const BOUND: Bound = Bound::Unbounded;
}


Full error message

[141851. 2025-02-03T04:35:44.409195124Z]: Panicked at 'Attempting to allocate an already allocated chunk.', /Users/ahmed/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ic-stable-structures-0.6.7/src/btreemap/allocator.rs:166:9
[141852. 2025-02-03T04:35:44.409195124Z]: [TRAP]: Panicked at 'Attempting to allocate an already allocated chunk.', /Users/ahmed/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ic-stable-structures-0.6.7/src/btreemap/allocator.rs:166:9
Canister Backtrace:
ic_cdk::api::trap
ic_cdk::printer::set_panic_hook::{{closure}}
std::panicking::rust_panic_with_hook
std::panicking::begin_panic_handler::{{closure}}
std::sys::backtrace::__rust_end_short_backtrace
rust_begin_unwind
core::panicking::panic_fmt
ic_stable_structures::btreemap::allocator::Allocator<M>::allocate
ic_stable_structures::btreemap::node::io::NodeWriter<M>::write
ic_stable_structures::btreemap::node::Node<K>::save
ic_stable_structures::btreemap::BTreeMap<K,V,M>::insert
std::thread::local::LocalKey<T>::with
backend::files_content::updates::multi_updates
ic_cdk::futures::spawn
canister_update multi_updates

My canister status

'dfx canister create frotnend --network ic'.
ahmed@AHMEDs-MacBook-Pro odoc % dfx canister status backend              
Canister status call result for backend.
Status: Running
Controllers: bnz7o-iuaaa-aaaaa-qaaaa-cai ggj4v-72tnz-rd673-mjjid-ljvyu-gdwyo-c2x7t-szji2-pfmoo-bhv7p-pae
Memory allocation: 0
Compute allocation: 0
Freezing threshold: 2_592_000
Memory Size: Nat(97108527)
Balance: 3_100_000_000_000 Cycles
Reserved: 0 Cycles
Reserved cycles limit: 5_000_000_000_000 Cycles
Wasm memory limit: 0 Bytes
Module hash: 0xf01d5dd2f2a889146ccb56fc7aed20dab8fe1f9ed14fe6890516f3f0dd6f3fe2
Number of queries: 5_988
Instructions spent in queries: 26_496_901_061
Total query request payload size (bytes): 75_004
Total query response payload size (bytes): 13_633_070
Log visibility: controllers
2 Likes

or I could lose my data.

Unfortunately, once you have two stable structures backed by the same memory, there is no guarantee that anything in there is safe.

is it safe to update just one of them?

If you only ever wrote to one of them, there might be a chance (although perhaps an expert in stable structure internals might rule that out too), so I suggest you try it in a test environment. But don’t get your hopes up, you likely have corrupted both stable structures.

1 Like