Skip to main content
Version: Testnet (v5.0.0-rc.1)

Debugging Aztec Code

This guide shows you how to debug issues in your Aztec development environment.

Prerequisites

  • Running Aztec local network
  • Aztec.nr contract or aztec.js application
  • Basic understanding of Aztec architecture

Enable logging

For adding log statements to your contracts, controlling log verbosity, and understanding the LOG_LEVEL syntax, see the Logging from Contracts guide.

To enable verbose system-level logging on a local network:

LOG_LEVEL=verbose aztec start --local-network

Debugging common errors

Contract Errors

ErrorSolution
Aztec dependency not foundAdd to Nargo.toml: aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v5.0.0-rc.1", directory="noir-projects/aztec-nr/aztec" }
Public state writes only supported in public functionsMove state writes to public functions
Unknown contract 0x0Call wallet.registerContract(...) to register contract
No public key registered for addressCall wallet.registerSender(...)
Direct invocation of ... functions is not supportedUse self.call(), self.view(), or self.enqueue() to call contract functions
Failed to solve brillig functionCheck function parameters and note validity
Cross-contract utility call deniedConfigure an authorizeUtilityCall execution hook on your PXE

Cross-contract utility call denied

Utility functions execute on the user's device and have access to private state. A cross-contract utility call made by a malicious or compromised contract could leak private information to an untrusted contract. PXE therefore denies cross- contract utility calls by default and requires explicit authorization via an execution hook. Calls to standard contracts (such as the HandshakeRegistry, which is queried during every contract's sync) are always automatically authorized.

When a contract executes a utility function that calls into a different contract, PXE asks an execution hook whether the call should be allowed. If no hook is configured, or the hook denies the request, you will see:

Cross-contract utility call denied: <reason>. <caller> attempted to call <target>:<selector> (<name>).
In production

Pass an authorizeUtilityCall hook when creating your PXE:

import { PXE } from "@aztec/pxe/server";

const pxe = await PXE.create({
// ...other options
hooks: {
authorizeUtilityCall: async (request) => {
// Inspect request.caller, request.target, request.functionSelector, etc.
return { authorized: true };
},
},
});

The hook receives a UtilityCallAuthorizationRequest with the caller and target addresses, their contract class IDs, function selector, function name, arguments, and caller context ('private', 'private view', or 'utility'). Return { authorized: true } to allow or { authorized: false, reason: '...' } to deny with a message.

In Noir tests

When testing cross-contract utility calls in the Noir test environment (TXE), use with_authorized_utility_call_targets on your call options:

// For private calls:
env.call_private_opts(
account,
CallPrivateOptions::new().with_authorized_utility_call_targets([target_address]),
MyContract::at(caller).some_private_fn(),
);

// For private view calls:
env.view_private_opts(
account,
ViewPrivateOptions::new().with_authorized_utility_call_targets([target_address]),
MyContract::at(caller).some_view_fn(),
);

// For utility calls:
env.execute_utility_opts(
ExecuteUtilityOptions::new().with_authorized_utility_call_targets([target_address]),
MyContract::at(caller).some_utility_fn(),
);

Circuit Errors

Error CodeMeaningFix
2002Invalid contract addressEnsure contract is deployed and address is correct
2005/2006Static call violationsRemove state modifications from static calls
2017User intent mismatchVerify transaction parameters match function call
3001Unsupported operationCheck if operation is supported in current context
3005Non-empty private call stackEnsure private functions complete before public
4007/4008Chain ID/version mismatchVerify L1 chain ID and Aztec version
7008Membership check failedEnsure using valid historical state
7009Array overflowReduce number of operations in transaction

Quick Fixes for Common Issues

# Archiver sync issues - force progress with dummy transactions.
# Assumes you have imported the local network test accounts
# (aztec-wallet import-test-accounts) and have a deployed token
# aliased as `testtoken`.
aztec-wallet send transfer --from test0 --contract-address testtoken --args accounts:test0 0
aztec-wallet send transfer --from test0 --contract-address testtoken --args accounts:test0 0

# L1 to L2 message pending - wait for inclusion
# Messages need 2 blocks to be processed

Debugging WASM errors

Enable debug WASM

// In vite.config.ts or similar
export default {
define: {
"process.env.BB_WASM_PATH": JSON.stringify("https://debug.wasm.url"),
},
};

Profile transactions

import { serializePrivateExecutionSteps } from "@aztec/stdlib";

// Profile the transaction
const profileTx = await contract.methods
.myMethod(param1, param2)
.profile({ profileMode: "execution-steps" });

// Serialize for debugging
const ivcMessagePack = serializePrivateExecutionSteps(profileTx.executionSteps);

// Download debug file
const blob = new Blob([ivcMessagePack]);
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "debug-steps.msgpack";
link.click();

⚠️ Warning: Debug files may contain private data. Use only in development.

Interpret error messages

Circuit and protocol errors

  • Private kernel errors (2xxx): Issues with private function execution
  • Public kernel errors (3xxx): Issues with public function execution
  • Rollup errors (4xxx): Block production issues
  • Generic errors (7xxx): Resource limits or state validation

Transaction limits

Current limits that trigger 7009 - ARRAY_OVERFLOW:

  • Max new notes per tx: Check MAX_NOTE_HASHES_PER_TX
  • Max nullifiers per tx: Check MAX_NULLIFIERS_PER_TX
  • Max function calls: Check call stack size limits
  • Max L2→L1 messages: Check message limits

Debugging sequencer issues

Common sequencer errors

ErrorCauseSolution
tree root mismatchState inconsistencyRestart local network or check state transitions
next available leaf index mismatchTree corruptionVerify tree updates are sequential
Public call stack size exceededToo many public callsReduce public function calls
Failed to publish blockL1 submission failedCheck L1 connection and gas

Reporting issues

When debugging fails:

  1. Collect error messages and codes
  2. Generate transaction profile (if applicable)
  3. Note your environment setup
  4. Create issue at aztec-packages

Quick reference

Enable verbose logging

LOG_LEVEL=verbose aztec start --local-network

Contract logging

See the full Logging from Contracts guide for all available log functions and LOG_LEVEL configuration.

use aztec::oracle::logging::{debug_log, debug_log_format};

Check contract registration

await wallet.getContractMetadata(myContractInstance.address);

Decode L1 errors

Check hex errors against Errors.sol

Tips

  • Always check logs before diving into circuit errors
  • State-related errors often indicate timing issues
  • Array overflow errors mean you hit transaction limits
  • Use debug WASM for detailed stack traces
  • Profile transactions when errors are unclear

Next steps