Wasm module defined ___ globals which exceeds the maximum number allowed 200

When deploying my wasm, I am getting this error:

The invocation to the wallet call forward method failed with the error: 
An error happened during the call: 5: 
Wasm module of canister xxxxx-xxxxx-xxxxx-xxxxx-xxx is not valid: 
Wasm module defined 220 globals which exceeds the maximum number allowed 200.

What do these globals refer to ?

I am trying to figure out where they come from, so I can fix my code.

I am using C++, and I linked in a 3rd party json library that triggered this error.

Are they these things?

1 Like

Try to use ic-cdk-optimizer for wasm optimization (this is Rust utility from Dfinity)

Example of usage

# Install
curl https://sh.rustup.rs -sSf | sh # rust install
cargo install ic-cdk-optimizer

# Build your wasm from C++

ic-cdk-optimizer ./your-compiled-module.wasm -o ./your-awesome-compiled-module.wasm
1 Like

It made the wasm 37% smaller, but all the 220 globals remain.

Good tip though! I am going to include that optimizer in my build pipeline. Thank you.

1 Like

Cool! :slight_smile:
In that case, I can only advice to find a smaller library or move necessary code to your repo.

These arbitrary limits on the system on what should be internals of the canister can and probably should be adjusted. Different CDKs likely produce quite differently looking modules, and I assume the limit was chosen based on canisters in the wild, so mostly Rust and Motoko. If it turns out that C++ tends to use globals more heavily, then hopefully we can get the limit increased quickly.

(Or simply be lifted, as long as the cost of globals is adequately represented in the storage cost for a canister.)

Pinging @diegop , because this should probably trickle into the internal discussions.


Yeah, I was feeling the same way. The C++ code is working perfectly fine, but I cannot deploy it to IC due to this limit.

I am trying to find out where these globals come from, so I might be able reduce them, but I have not figured that out yet.

One guess may be function pointers across units of compilation. LLVM represents entries in something called Global Offset Table using globals. But my memory is vague and it’s late, so it might be something else.

There is some information in the note at

If you have some form of link-time-optimization that you can enable that might help?

Are these globals mutable or immutable? Ideally the system doesn’t store and thus charge for immutable globals, but I didn’t check.

What I am trying to do is link in a json library. I found a way to use C++ with std::string, so my thinking was that I now can use JSON data by using Candid text → std::string → JSON → do stuff → JSON → std::string → Candid text.

I tried 3 different versions, which all are pretty small & standalone:

I think that I need to stick to lower level C libraries for now, and wrap them myself into Classes that somehow do not generate globals. I am still studying what is generating these globals, but it does seem to be caused by function pointers, traits, virtual things & such…


Im sure you are using WABT already, but if not there a lot of powerful tools for debugging these issues. Here is a wasm2wat online which can be useful: wasm2wat demo

1 Like

Yeah, I had already discovered WABT. Great tools indeed!

I am having good luck with cJSON, and can send json text back & forth. For now, this will work, but I will probably hit the globals limit again soon.

1 Like

@3cL1p5e7 ,
I have a follow-up question on ic-cdk-optimizer.

I added it to my github actions workflow, but the cargo install ic-cdk-optimizer step takes 7 minutes (!). I am using the ubuntu-latest runner.

I did some searching, but could not find a way to speed this up. Do you happen to know a trick?

Someone who knows rust well, correct me please

As I know, cargo can not download precompiled packages and compile crates directly on machine.
Nevertheless, you can try using github actions cache between jobs and put cargo caches there. More info about installed crates location
Here is another useful link with a similar problem

(Reposting here from something I vaguely remember saying at the dev tools WG, at @Fulco’s request:)

The issue here is that the things other programs think of as translators from code to binary, like the linker, C++ thinks of as a direct build tool, as integral as rustc. Exported symbols are how other programs perform FFI; they’re how C++ works across files at all. This can result in some surprising behavior in environments that have requirements on things like the number of exported symbols. There is a SO post on the subject of controlling exports which you may find helpful to read - one of those strategies is likely to work in solving your problem.


I’d like to raise this issue again, because another user of C++ CDK recently ran into this limit.

See here, it is related to the ostream operator. They had to strip out a library.

My experience from integrating several libraries is that the ostream is used very, very often to make the library work on a variety of systems. I had to hack just that piece of code, and then it worked.

I haven’t yet seen the limit go above 750, and I wonder if raising it to 1,000 would be a possibility, so many more C++ libraries would just work for the IC

1 Like

Down with the limits! Out with them!

I’ve hit similar issues in the past with Azle/Kybra, let’s get rid of them.

1 Like

@diegop , @domwoe ,

Today I learned about yet another C++ library that compiled fine to Webassembly running in a canister, except that the user had to work around the globals limit.

Would it be possible to start a discussion how this limit can be removed?

This is the project with the library in question.

1 Like

I dont see why not. You can start a thread and I think @dsarlis would know much more about the WebAssembly side.


I hope this limit gets removed as well, it would make 3rd party libraries build process very smooth.


Hey folks,
We’re lifting the limit to 1000. The change should be merged ~today and rolled out by the next week.