Error Handling
Common errors and how to handle them.
Connection Errors
"Wallet not connected"
try {
await client.connect({ mode: 'linked', walletClient });
} catch (error) {
if (error.message.includes('Wallet not connected')) {
// Prompt user to connect wallet first
}
}Solution: Ensure wallet is connected before calling connect().
"User rejected signature"
The user declined the EIP-712 signature request.
Solution: Show a message explaining why the signature is needed.
Sync Errors
"Failed to fetch commitments"
RPC connection issue.
try {
await client.sync();
} catch (error) {
if (error.message.includes('Failed to fetch')) {
// RPC might be down, retry or use fallback
}
}Solutions:
- Check network connectivity
- Verify RPC URL is correct
- Try a different RPC provider
"Root mismatch"
QuickSync data doesn't match on-chain state.
Solution: Clear cache and resync:
await client.clearCache();
await client.sync();Transfer Errors
"Insufficient balance"
Not enough funds for the transfer.
const balance = client.getBalanceByTokenId(tokenId);
if (balance < amount) {
throw new Error(`Need ${amount}, have ${balance}`);
}Solution: Check balance before transferring.
"No spendable notes"
You have balance but notes aren't spendable yet.
Causes:
- Notes not yet synced
- Notes already spent (sync needed)
- Wrong token ID
Solution:
await client.sync();
const notes = client.getUnspentNotes(tokenId);
console.log('Spendable notes:', notes.length);"Proof generation failed"
Circuit execution error.
Causes:
- Invalid inputs
- Memory exhaustion
- Circuit bug
Solutions:
- Ensure notes exist and aren't spent
- Close other browser tabs (memory)
- Check console for detailed error
"Nullifier already spent"
Attempted double-spend.
Cause: Local state out of sync with chain.
Solution:
await client.sync();
// Retry transferUnshield Errors
"Invalid recipient address"
The EOA address is malformed.
import { isAddress } from 'viem';
if (!isAddress(recipient)) {
throw new Error('Invalid Ethereum address');
}"Withdrawal exceeds balance"
Trying to unshield more than available.
Solution: Check balance first.
Prover Errors
"Circuit not found"
Circuit file couldn't be loaded.
try {
const prover = await createProver({
transferCircuit: '/circuits/transfer.json',
unshieldCircuit: '/circuits/unshield.json',
});
} catch (error) {
if (error.message.includes('not found') || error.message.includes('404')) {
console.error('Circuit files missing from public folder');
}
}Solution: Ensure circuit JSON files are in the correct location.
"WASM initialization failed"
WASM module couldn't load.
Causes:
- Browser doesn't support WASM
- CSP blocking WASM
- Network error
Solutions:
- Use modern browser
- Check Content-Security-Policy headers
- Ensure WASM files are served correctly
"Out of memory"
Prover ran out of memory.
Solutions:
- Close other tabs
- Use desktop instead of mobile
- Consider using relayer
Relayer Errors
"Relayer unavailable"
Relayer service is down.
Solution: Fall back to direct submission or retry later.
"Transaction rejected by relayer"
Relayer refused to submit transaction.
Causes:
- Invalid proof
- Spent nullifiers
- Rate limiting
Address Errors
"Invalid ZK address"
The zks1... address is malformed.
import { decodeAddress } from '@zkprivacy/sdk';
try {
decodeAddress(address);
} catch {
throw new Error('Invalid ZK address format');
}Generic Error Handler
async function safeTransfer(params: TransferRequest) {
try {
return await client.transfer(params);
} catch (error) {
const message = (error as Error).message;
if (message.includes('Insufficient balance')) {
throw new Error('Not enough funds for this transfer');
}
if (message.includes('Proof generation failed')) {
throw new Error('Failed to generate proof. Please try again.');
}
if (message.includes('Nullifier already spent')) {
await client.sync();
throw new Error('State out of sync. Please try again.');
}
// Unknown error
console.error('Transfer error:', error);
throw new Error('Transfer failed. Please try again later.');
}
}Debugging Tips
Enable Verbose Logging
// Check browser console for detailed logs
localStorage.setItem('debug', 'zkprivacy:*');Check Note State
const notes = client.getUnspentNotes();
console.log('Notes:', notes.map(n => ({
amount: n.note.amount,
tokenId: n.tokenId,
leafIndex: n.leafIndex,
})));Verify Sync State
const address = client.getAddress();
const balance = client.getBalance();
console.log('Address:', address);
console.log('Balance:', balance);