Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cloak.ag/llms.txt

Use this file to discover all available pages before exploring further.

SDK error surfaces

@cloak.dev/sdk exposes two main layers:
  1. CloakError (runtime SDK operations)
  2. parseError / parseTransactionError (user-facing normalization)

CloakError

import { CloakError } from "@cloak.dev/sdk";

// category values:
// "network" | "indexer" | "prover" | "relay" | "validation" | "wallet" | "environment"
Most app flows should treat retryable === true as backoff-eligible.

Stale Merkle root (0x1001)

Use helpers for RootNotFound detection:
import { isRootNotFoundError, RootNotFoundError } from "@cloak.dev/sdk";

try {
await sdk.withdraw(connection, note, recipient);
} catch (error) {
if (error instanceof RootNotFoundError || isRootNotFoundError(error)) {
// Refresh proof inputs and retry flow
}
}

SDK note and UTXO flows already include retry loops; this usually appears only after retries are exhausted.

On-chain error message mapping

ShieldPoolErrors maps known custom program codes:
import { ShieldPoolErrors } from "@cloak.dev/sdk";

console.log(ShieldPoolErrors[0x1001]); // Root not found in the roots ring
console.log(ShieldPoolErrors[0x1076]); // Proof verification failed

User-facing normalization

import { parseError, type UserFriendlyError } from "@cloak.dev/sdk";

try {
await sdk.swap(connection, note, recipient, swapOptions);
} catch (error) {
const parsed: UserFriendlyError = parseError(error);
// parsed.category: wallet | network | validation | service | transaction | unknown
// parsed.recoverable: boolean
}

Practical retry strategy

  • Retry network and service failures with exponential backoff.
  • Avoid retrying deterministic validation failures.
  • On stale-root errors, rebuild proof data before retrying.

UTXO stale-root retry template

import { fullWithdraw, isRootNotFoundError } from "@cloak.dev/sdk";

let result: Awaited<ReturnType<typeof fullWithdraw>> | undefined;
let cachedMerkleTree = initialMerkleTree;

for (let attempt = 1; attempt <= 3; attempt += 1) {
  try {
    result = await fullWithdraw(inputUtxos, recipientWallet, {
      connection,
      programId,
      relayUrl,
      walletPublicKey,
      signMessage,
      cachedMerkleTree,
    });
    break;
  } catch (error) {
    if (!isRootNotFoundError(error) || attempt === 3) throw error;
    await new Promise((resolve) => setTimeout(resolve, 1_500 * attempt));
    // refresh proof inputs before retrying (new root + updated tree)
    cachedMerkleTree = undefined;
  }
}

if (!result) throw new Error("withdraw did not complete");

Security logging rule

Never log full notes/UTXOs with secrets. Log only non-sensitive identifiers (commitment prefix, leaf index, request ID, and tx signature when needed for support).