> For the complete documentation index, see [llms.txt](https://docs.baas.sh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.baas.sh/baas-sdk/resources/error-handling.md).

# Error handling

Every error thrown by the SDK extends `BaasError` and carries a stable `code`. You can branch on the error class with `instanceof`, or on the `code` for finer cases.

```ts
import { BaasAuthError, BaasWalletError } from '@baas/sdk';

try {
  await baas.auth.signInWithPasskey();
} catch (err) {
  if (err instanceof BaasWalletError) {
    // user cancelled the passkey/wallet dialog — show a "try again" UI
  } else if (err instanceof BaasAuthError) {
    // err.code: 'NONCE_FETCH_FAILED' | 'VERIFY_FAILED'
    console.error('Sign-in failed:', err.code, err.message);
  }
}
```

## Error codes

| Code                      | Class              | Meaning                                                                                                                                                    |
| ------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `WALLET_CONNECT_REJECTED` | `BaasWalletError`  | The user cancelled the wallet or passkey prompt.                                                                                                           |
| `NONCE_FETCH_FAILED`      | `BaasAuthError`    | The sign-in handshake couldn't start (network, timeout, or non-2xx). The wallet never opened.                                                              |
| `VERIFY_FAILED`           | `BaasAuthError`    | Verification failed (network, timeout, or non-2xx — e.g. invalid signature or unsupported chain).                                                          |
| `REFRESH_FAILED`          | `BaasAuthError`    | A token refresh failed for a non-401 reason (network down, 5xx…). The session is kept; retry is safe.                                                      |
| `SESSION_EXPIRED`         | `BaasAuthError`    | The refresh cookie expired or is invalid. The session is cleared — the user must sign in again.                                                            |
| `NOT_SIGNED_IN`           | `BaasAuthError`    | An authenticated action was attempted with no active session. Thrown synchronously.                                                                        |
| `NETWORK_ERROR`           | `BaasNetworkError` | `fetch` threw or the request timed out.                                                                                                                    |
| `API_ERROR`               | `BaasApiError`     | A non-2xx HTTP response. Exposes `.status` and `.body` (plus `.level` and `.validOptions` for smart contract validation errors — see below).               |
| `INVALID_CONFIG`          | `BaasError`        | `projectId`, `publishableKey`, or `apiUrl` missing at init; `signIn('wallet')` called without a provider; or the lazy `/networks` list is empty/malformed. |
| `UNKNOWN_CHAIN`           | `BaasError`        | `baas.chains.switch(chainId)` was called with a chain not in `list()`.                                                                                     |
| `TRANSACTION_REJECTED`    | `BaasWalletError`  | The user cancelled a `sendTransaction` wallet prompt (EIP-1193 `4001`).                                                                                    |
| `TRANSACTION_FAILED`      | `BaasError`        | `eth_sendTransaction` failed for a non-user reason. `err.cause` carries the original.                                                                      |
| `SIGNATURE_REJECTED`      | `BaasWalletError`  | The user cancelled a `signMessage` wallet prompt.                                                                                                          |
| `SIGNATURE_FAILED`        | `BaasError`        | `personal_sign` failed for a non-user reason. `err.cause` carries the original.                                                                            |
| `SIGNER_REQUIRED`         | `BaasError`        | The user signed in with an external wallet but a wallet op was called without `{ signer }`. Pass the same EIP-1193 provider used at sign-in.               |

{% hint style="info" %}
Passkey and wallet sign-in **never** throw `SESSION_EXPIRED`. A stale cookie just falls through to the sign-in dialog. `SESSION_EXPIRED` is only thrown by an explicit `refresh()` when the cookie is invalid.
{% endhint %}

{% hint style="info" %}
**Network errors can surface at sign-in.** The SDK loads your project's networks lazily, on the first passkey sign-in (and on `baas.chains.list()` / `switch()`). So `signInWithPasskey()`, `signUpWithPasskey()`, and the `chains` methods can throw `NETWORK_ERROR`, `API_ERROR`, or `INVALID_CONFIG` from that fetch (separate from a user cancelling, `WALLET_CONNECT_REJECTED`). `signInWithWallet()` never touches `/networks`, so it never throws these.
{% endhint %}

## Smart contract validation errors

When you call `baas.contract(...)` with an unknown class name, address, function, or argument list, BaaS returns a `BaasApiError` carrying two extra properties to help you guide users:

* `.level: 'contract' | 'address' | 'function' | 'params'` — the step that failed.
* `.validOptions: string[]` — what was valid at that step.

```ts
try {
  await baas.contract('ERC20x').address('0x…').function('mint').params(1000).read();
} catch (err) {
  if (err instanceof BaasApiError && err.level === 'contract') {
    console.log('Did you mean:', err.validOptions);
    // → ['ERC20', 'ERC721', 'MyToken']
  }
}
```

| `.level`     | HTTP   | Meaning                                                               | `.validOptions`                                        |
| ------------ | ------ | --------------------------------------------------------------------- | ------------------------------------------------------ |
| `'contract'` | 404    | Unknown class name                                                    | All registered class names                             |
| `'address'`  | 404    | No deployment at this address under that class                        | Deployments for the requested class                    |
| `'function'` | 400    | Function not declared in the ABI                                      | Function names declared in the ABI                     |
| `'params'`   | 400    | Argument count or type mismatch (see `err.body.code` for the subcase) | Type-only or full signatures, depending on the subcase |
| `undefined`  | varies | Non-cascade error (revert, network, etc.)                             | —                                                      |

The `undefined` row covers everything else: a smart contract revert (still a `BaasApiError`), a network failure (`BaasNetworkError`, which doesn't have `.level` at all), a user cancellation (`BaasWalletError`, same). In all cases, `if (err.level)` is a safe truthy check to decide whether to surface suggestions.

When `.level === 'params'`, `err.body.code` distinguishes two cases:

* **`params_mismatch`** — no overload has the right number of arguments, or a type couldn't be coerced. `validOptions` lists types-only signatures like `'(uint256)'` or `'(address,uint256)'`.
* **`params_ambiguous`** — multiple overloads match by argument count and need a `Typed.*` wrapper to pick the right one. `validOptions` lists full overload names like `'value(uint8)'` and `'value(uint256)'`. See [Disambiguating overloaded functions](/baas-sdk/blockchain/smart-contracts.md#disambiguating-overloaded-functions) for the recipe.

<details>

<summary>Inspecting the underlying cause</summary>

When the SDK wraps a lower-level error, it keeps the original on `.cause` (the ES2022 standard). This is how you reach details like an HTTP status code:

```ts
if (err instanceof BaasAuthError && err.code === 'VERIFY_FAILED') {
  if (err.cause instanceof BaasApiError && err.cause.status === 429) {
    // rate-limited — surface a "slow down" message
  }
}
```

There's no dedicated code for rate limiting (429); read it from `.cause.status` when you need it.

</details>

## Next

* [Exported types](/baas-sdk/resources/exported-types.md) — import the error classes you use with `instanceof`.
* [API reference](/baas-sdk/resources/api-reference.md) — method signatures and which errors they can throw.
* [Troubleshooting](/baas-sdk/troubleshooting.md) — common pitfalls and how to resolve them.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.baas.sh/baas-sdk/resources/error-handling.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
