Common issues and solutions
Troubleshooting guide for frequently encountered Stylus development issues
New Stylus activations are paused
The Arbitrum Security Council has temporarily paused new Stylus contract activations on Arbitrum One and Arbitrum Nova. Stylus contracts that are already activated keep running until they expire. To learn what this means for you, read Temporary pause on new Stylus activations.
This guide covers frequently encountered issues in Stylus development with step-by-step solutions.
Installation and setup
cargo stylus not found
Problem: Running cargo stylus returns "command not found."
Solution:
# Install cargo-stylus
cargo install --force cargo-stylus
# Verify installation
cargo stylus --version
# If still not found, check your PATH includes ~/.cargo/bin
echo $PATH | grep cargoAlternative: Add cargo bin to your PATH:
# Add to ~/.bashrc or ~/.zshrc
export PATH="$HOME/.cargo/bin:$PATH"
# Reload shell configuration
source ~/.bashrc # or source ~/.zshrcWASM target not installed
Problem: Build fails with "can't find crate for std" or "target 'wasm32-unknown-unknown' not found."
Solution:
# Add WASM target for your Rust version
rustup target add wasm32-unknown-unknown
# If using specific toolchain
rustup target add wasm32-unknown-unknown --toolchain 1.91Verify:
rustup target list | grep wasm32
# Should show: wasm32-unknown-unknown (installed)Docker not running
Problem: cargo stylus check fails with "Cannot connect to Docker daemon."
Solution:
- Start Docker Desktop
- Verify Docker is running:
docker ps- If still failing, restart Docker:
# macOS/Linux
sudo systemctl restart docker
# Or restart Docker Desktop applicationBuild errors
Contract exceeds size limit
Problem: "Program activation failed: program too large."
Solution:
- Optimize build settings in
Cargo.toml:
[profile.release]
opt-level = "z" # Optimize for size
lto = true # Link-time optimization
codegen-units = 1 # Better optimization
strip = true # Remove debug symbols
panic = "abort" # Smaller panic handling- Run
wasm-opton the compiled binary:
wasm-opt -Oz target/wasm32-unknown-unknown/release/your_contract.wasm \
-o optimized.wasm- Remove unused dependencies:
# Remove unnecessary features
stylus-sdk = { version = "0.10.7", default-features = false }- Check binary size:
ls -lh target/wasm32-unknown-unknown/release/*.wasmSee Optimizing binaries guide for more strategies.
Linker errors
Problem: Build fails with linker errors or "undefined reference" messages.
Solution:
# Clean build artifacts
cargo clean
# Rebuild
cargo build --target wasm32-unknown-unknown --release
# If still failing, update Rust
rustup updateDependency compilation failures
Problem: Dependencies fail to compile for WASM target.
Solution:
-
Check dependency compatibility: Not all crates support WASM. Look for:
no_stdsupport- WASM-compatible versions
- Alternatives designed for blockchain
-
Disable incompatible features:
[dependencies]
# Disable std-only features
serde = { version = "1.0", default-features = false, features = ["derive"] }- Use Stylus-compatible alternatives: See recommended libraries.
Deployment errors
Insufficient funds
Problem: "Insufficient funds for gas * price + value."
Solution:
# Check your balance
cast balance <YOUR_ADDRESS> --rpc-url <RPC_URL>
# Fund your account (testnet)
# Visit faucet: https://faucet.quicknode.com/arbitrum/sepoliaActivation failed
Problem: "Program activation failed" after deployment.
Solution:
- Check contract size:
cargo stylus check- Verify WASM validity:
# Ensure binary is valid WASM
wasm-validate target/wasm32-unknown-unknown/release/*.wasm- Check gas limits:
# Increase gas limit for activation
cargo stylus deploy \
--endpoint=<RPC_URL> \
--private-key=<KEY> \
--gas-limit=10000000See Activation concepts for more details.
Transaction reverted
Problem: Deployment transaction reverts without clear error.
Solution:
- Enable verbose logging:
RUST_LOG=debug cargo stylus deploy \
--endpoint=<RPC_URL> \
--private-key=<KEY>- Check network status:
# Verify RPC is responding
cast block-number --rpc-url <RPC_URL>- Use replay for debugging:
cargo stylus replay \
--endpoint=<RPC_URL> \
<TRANSACTION_HASH>Runtime errors
Panic in contract code
Problem: Contract panics during execution.
Solution:
- Use
Resultinstead ofpanic!:
// ❌ Bad: Panics
pub fn divide(&self, a: U256, b: U256) -> U256 {
if b.is_zero() {
panic!("Division by zero");
}
a / b
}
// ✅ Good: Returns error
pub fn divide(&self, a: U256, b: U256) -> Result<U256, Vec<u8>> {
if b.is_zero() {
return Err(b"Division by zero".to_vec());
}
Ok(a / b)
}- Enable backtrace for debugging:
RUST_BACKTRACE=1 cargo testPanics produce a dataless revert
Panicking and other hard errors in Stylus programs cause a dataless revert — the transaction reverts but no error data is returned to the caller. If you need rich error messages onchain, return Result<T, Vec<u8>> from your function and encode the error explicitly rather than relying on panic!.
Storage slot conflicts
Problem: Storage values overwriting each other unexpectedly.
Solution:
- Use explicit storage layout:
sol_storage! {
pub struct MyContract {
#[borrow] // Use borrow for nested structs
StorageMap<Address, UserData> users;
StorageU256 total_supply;
}
}-
Avoid manual slot manipulation unless necessary.
-
Check for storage collisions in upgradeable contracts.
Out of gas
Problem: Transactions fail with "out of gas" error.
Solution:
- Optimize loops:
// ❌ Bad: Unbounded loop
pub fn process_all(&self, items: Vec<Address>) -> Result<(), Vec<u8>> {
for item in items {
self.process(item)?; // Could exceed gas limit
}
Ok(())
}
// ✅ Good: Paginated
pub fn process_batch(&self, items: Vec<Address>, max: usize) -> Result<(), Vec<u8>> {
for item in items.iter().take(max) {
self.process(*item)?;
}
Ok(())
}- Increase gas limit when calling:
cast send <CONTRACT> "function()" \
--gas-limit 1000000 \
--rpc-url <RPC_URL> \
--private-key <KEY>- Profile gas usage:
cargo stylus trace <TX_HASH> --endpoint <RPC_URL>See Gas optimization guide for more strategies.
Testing issues
Tests failing with storage errors
Problem: Tests fail with "storage not initialized" or similar errors.
Solution:
#[cfg(test)]
mod tests {
use super::*;
use stylus_sdk::testing::*;
#[test]
fn test_contract() {
// ✅ Always initialize TestVM
let vm = TestVM::default();
let mut contract = MyContract::from(&vm);
// Configure test environment
vm.set_sender(address!("0x0000000000000000000000000000000000000001"));
// Run tests
assert_eq!(contract.get_value().unwrap(), U256::ZERO);
}
}Tests pass locally but fail on CI
Problem: Tests work on your machine but fail in continuous integration.
Solution:
- Pin Rust version in
rust-toolchain.toml:
[toolchain]
channel = "1.91.0"
components = ["rustfmt", "clippy"]
targets = ["wasm32-unknown-unknown"]- Ensure WASM target in CI:
# .github/workflows/test.yml
- name: Add WASM target
run: rustup target add wasm32-unknown-unknown- Check for environment-specific dependencies.
ABI and integration
ABI export fails
Problem: cargo stylus export-abi returns empty or incorrect ABI.
Solution:
- Ensure methods use
#[public]macro:
#[public]
impl MyContract {
// ✅ This will be exported
pub fn public_method(&self) -> U256 {
U256::from(42)
}
// ❌ This won't be exported (no #[public])
fn internal_method(&self) -> U256 {
U256::from(42)
}
}- Check for compilation errors:
cargo build --target wasm32-unknown-unknown --release
cargo stylus export-abiType conversion errors
Problem: "Type mismatch" when calling from Solidity or TypeScript.
Solution:
- Use explicit type conversions:
use stylus_sdk::abi::AbiType;
// ✅ Ensure types match Solidity ABI
pub fn get_balance(&self, account: Address) -> U256 {
self.balances.get(account)
}- Check ABI matches expectations:
cargo stylus export-abi > abi.json
# Verify types in abi.json match your frontendNetwork and RPC issues
RPC connection timeout
Problem: "Connection timeout" or "Connection refused" errors.
Solution:
- Verify RPC URL:
# Test RPC connection
curl -X POST <RPC_URL> \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'-
Check rate limits: Public RPCs may have rate limits. Consider:
- Using a dedicated RPC provider
- Adding retry logic
- Implementing backoff strategies
-
Network-specific endpoints:
- Arbitrum Sepolia: https://sepolia-rollup.arbitrum.io/rpc
- Arbitrum One: https://arb1.arbitrum.io/rpc
Nonce errors
Problem: "Nonce too low" or "nonce too high" errors.
Solution:
- Let tooling manage nonces (default behavior):
# cargo stylus automatically manages nonces
cargo stylus deploy --endpoint <RPC_URL> --private-key <KEY>- Reset nonce manually if needed:
# Check current nonce
cast nonce <YOUR_ADDRESS> --rpc-url <RPC_URL>
# Send transaction with specific nonce
cast send <CONTRACT> "function()" \
--nonce <NONCE> \
--rpc-url <RPC_URL> \
--private-key <KEY>Debugging strategies
Enable verbose logging
# Set log level
export RUST_LOG=debug
# Run with logging
cargo stylus check
cargo stylus deploy --endpoint <RPC_URL> --private-key <KEY>Use replay debugging
# Replay failed transaction
cargo stylus replay \
--endpoint <RPC_URL> \
<TX_HASH>
# With GDB for advanced debugging
cargo stylus replay \
--endpoint <RPC_URL> \
--use-gdb \
<TX_HASH>See Debugging guide for detailed walkthrough.
Trace execution
# Trace transaction execution
cargo stylus trace <TX_HASH> --endpoint <RPC_URL>Check contract state
# Read storage values
cast storage <CONTRACT_ADDRESS> <SLOT> --rpc-url <RPC_URL>
# Call view functions
cast call <CONTRACT_ADDRESS> "getValue()(uint256)" --rpc-url <RPC_URL>Getting help
If you're still stuck:
- Check documentation: Search Stylus docs
- Search GitHub issues: Stylus SDK Issues
- Ask in Discord: Arbitrum Discord #stylus channel
- Post in forums: Arbitrum Developer Forum
Creating a good bug report
Include:
- Rust version:
rustc --version - cargo-stylus version:
cargo stylus --version - Minimal reproducible example
- Full error message and stack trace
- Steps to reproduce
- Expected vs. actual behavior
Common error messages quick reference
| Error Message | Common Cause | Quick Fix |
|---|---|---|
| "target not found: wasm32-unknown-unknown" | WASM target not installed | rustup target add wasm32-unknown-unknown |
| "Cannot connect to Docker daemon" | Docker not running | Start Docker Desktop |
| "Program too large" | Binary exceeds size limit | Add optimization flags, run wasm-opt |
| "Insufficient funds" | Not enough ETH for gas | Fund account from faucet |
| "Nonce too low" | Nonce mismatch | Let tooling manage nonces |
| "Out of gas" | Gas limit exceeded | Increase gas limit or optimize code |
| "Storage not initialized" | Missing TestVM setup | Initialize TestVM::default() in tests |
| "Division by zero" | Unchecked arithmetic | Return an error or use checked_div() |
Next steps
- Review security best practices
- Study gas optimization
- Learn debugging techniques
- Explore testing strategies
How is this guide?