Skip to content

Error Handling

Common errors and how to handle them.

Connection Errors

"Wallet not connected"

typescript
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.

typescript
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:

typescript
await client.clearCache();
await client.sync();

Transfer Errors

"Insufficient balance"

Not enough funds for the transfer.

typescript
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:

typescript
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:

typescript
await client.sync();
// Retry transfer

Unshield Errors

"Invalid recipient address"

The EOA address is malformed.

typescript
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.

typescript
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.

typescript
import { decodeAddress } from '@zkprivacy/sdk';

try {
  decodeAddress(address);
} catch {
  throw new Error('Invalid ZK address format');
}

Generic Error Handler

typescript
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

typescript
// Check browser console for detailed logs
localStorage.setItem('debug', 'zkprivacy:*');

Check Note State

typescript
const notes = client.getUnspentNotes();
console.log('Notes:', notes.map(n => ({
  amount: n.note.amount,
  tokenId: n.tokenId,
  leafIndex: n.leafIndex,
})));

Verify Sync State

typescript
const address = client.getAddress();
const balance = client.getBalance();
console.log('Address:', address);
console.log('Balance:', balance);

Released under the MIT License.