> ## Documentation Index
> Fetch the complete documentation index at: https://zksync-sdk.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Error model

> Typed, structured errors with a stable envelope across viem and ethers adapters.

## Overview

All SDK operations either:

1. **Throw** a `ZKsyncError` whose `.envelope` gives you a structured, stable payload, or
2. Return a **result object** from the `try*` variants: `{ ok: true, value } | { ok: false, error }`.

This is consistent across both **ethers** and **viem** adapters.

<Tip>
  Prefer the <code>try\*</code> variants when you want to avoid exceptions and branch on
  success/failure.
</Tip>

## What gets thrown

When the SDK throws, it throws an instance of `ZKsyncError`. Use `isZKsyncError(e)` to narrow and read the **error envelope**.

```ts theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
import { isZKsyncError } from '@dutterbutter/zksync-sdk/core';

try {
  const handle = await sdk.deposits.create(params);
} catch (e) {
  if (isZKsyncError(e)) {
    const err = e; // type-narrowed
    const { type, resource, operation, message, context, revert } = err.envelope;

    // Example: route on category
    switch (type) {
      case 'VALIDATION':
      case 'STATE':
        // user/action fixable (bad input, not-ready, etc.)
        break;
      case 'EXECUTION':
      case 'RPC':
        // network/tx/provider issues
        break;
    }

    // Optional: log structured data
    console.error(JSON.stringify(err.toJSON()));
  } else {
    // Non-SDK error (framework, userland)
    throw e;
  }
}
```

## Envelope shape

<ResponseField name="name" type="'ZKsyncError'">
  Instance type for all SDK-thrown errors.
</ResponseField>

### `ZKsyncError.envelope: ErrorEnvelope`

```ts theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
type ErrorEnvelope = {
  /** Resource surface that raised the error. */
  resource: 'deposits' | 'withdrawals' | 'withdrawal-finalization' | 'helpers' | 'zksrpc';

  /** Specific operation, e.g. "withdrawals.finalize" or "deposits.create". */
  operation: string;

  /** Broad category (see table below). */
  type: 'VALIDATION' | 'STATE' | 'EXECUTION' | 'RPC' | 'INTERNAL' | 'VERIFICATION' | 'CONTRACT';

  /** Stable, human-readable message for developers. */
  message: string;

  /** Optional contextual fields (tx hash, nonce, step key, etc.). */
  context?: Record<string, unknown>;

  /** If the error is a contract revert, adapters include decoded info when available. */
  revert?: {
    selector: `0x${string}`; // 4-byte selector
    name?: string; // Decoded Solidity error name
    args?: unknown[]; // Decoded args
    contract?: string; // Best-effort contract label
    fn?: string; // Best-effort function label
  };

  /** Originating error (provider/transport/etc.), sanitized for safe logging. */
  cause?: unknown;
};
```

### Categories (when to expect them)

| Type           | Meaning (how you should react)                                                             |
| :------------- | :----------------------------------------------------------------------------------------- |
| `VALIDATION`   | Inputs are invalid (fix parameters and retry).                                             |
| `STATE`        | Operation not possible **yet** (e.g., not finalizable). Wait or change state.              |
| `EXECUTION`    | A send/revert happened (tx reverted or couldn’t be confirmed). Inspect `revert` / `cause`. |
| `RPC`          | Provider/transport failure (endpoint/network issue). Retry with backoff / check infra.     |
| `VERIFICATION` | Proof/verification step issue. Usually indicates unable to find deposit log.               |
| `CONTRACT`     | A contract read/encode/allowance failed. Check addresses & ABI compatibility.              |
| `INTERNAL`     | SDK internal error (please report with `operation` + `selector` if present).               |

## Result style (`try*`) helpers

Every resource method has a `try*` sibling that never throws and returns a `TryResult<T>`.

```ts theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
const res = await sdk.withdrawals.tryCreate(params);
if (!res.ok) {
  // res.error is a ZKsyncError
  console.warn(res.error.envelope.message, res.error.envelope.operation);
} else {
  // res.value is the success payload
  console.log('l2TxHash', res.value.l2TxHash);
}
```

This is especially handy for UI flows where you want to surface inline validation/state messages without a `try/catch`.

## Revert details (when transactions fail)

If the provider exposes revert data, the adapters will decode common error types and ABIs so you can branch on them:

```ts theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
try {
  await sdk.withdrawals.finalize(l2TxHash);
} catch (e) {
  if (isZKsyncError(e) && e.envelope.revert) {
    const { selector, name, args } = e.envelope.revert;
    // e.g., name === 'InvalidProof' or 'TransferAmountExceedsBalance'
  }
}
```

Notes:

* The SDK always includes the **4-byte selector**.
* `name`/`args` appear when decodable against known ABIs; coverage will expand over time.
* When a revert implies “not ready yet,” you’ll typically see a `STATE` error with a clarifying `message`.

## Ethers & viem examples

<CodeGroup>
  ```ts title="Ethers" theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
  import { JsonRpcProvider, Wallet } from 'ethers';
  import { createEthersClient, createEthersSdk } from '@dutterbutter/zksync-sdk/ethers';
  import { isZKsyncError } from '@dutterbutter/zksync-sdk/core';

  const l1 = new JsonRpcProvider(process.env.ETH_RPC!);
  const l2 = new JsonRpcProvider(process.env.ZKSYNC_RPC!);
  const signer = new Wallet(process.env.PRIVATE_KEY!, l1);

  const client = createEthersClient({ l1, l2, signer });
  const sdk = createEthersSdk(client);

  const res = await sdk.deposits.tryCreate({ token, amount, to });
  if (!res.ok) {
  // report envelope
  console.error(res.error.envelope);
  }

  ```

  ```ts title="Viem" theme={"theme":{"light":"vitesse-light","dark":"tokyo-night"}}
  import { createPublicClient, http, createWalletClient, privateKeyToAccount } from 'viem';
  import { createViemClient, createViemSdk } from '@dutterbutter/zksync-sdk/viem';
  import { isZKsyncError } from '@dutterbutter/zksync-sdk/core';

  const account = privateKeyToAccount(process.env.PRIVATE_KEY! as `0x${string}`);
  const l1 = createPublicClient({ transport: http(process.env.ETH_RPC!) });
  const l2 = createPublicClient({ transport: http(process.env.ZKSYNC_RPC!) });
  const l1Wallet: WalletClient<Transport, Chain, Account> = createWalletClient({
      account,
      transport: http(ETH_RPC),
  });
  const l2Wallet = createWalletClient<Transport, Chain, Account>({
      account,
      transport: http(ZKSYNC_RPC),
  });

  const client = createViemClient({ l1, l2, l1Wallet, l2Wallet });
  const sdk = createViemSdk(client);

  try {
    await sdk.withdrawals.finalize(l2TxHash);
  } catch (e) {
    if (isZKsyncError(e)) {
      console.log(e.envelope.message, e.envelope.operation);
    } else {
      throw e;
    }
  }
  ```
</CodeGroup>

## Logging & observability

* `err.toJSON()` returns a safe, structured object you can ship to logs/telemetry.
* For local debugging, printing `err` shows a compact, human-readable view (category, operation, context, optional revert/cause).

<Warning>
  Avoid parsing <code>err.message</code> for logic. Use the typed fields on{' '}
  <code>err.envelope</code> instead.
</Warning>
