> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arc.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Error handling for Borrow

> Borrow error codes, their categories, and how to respond to them

The App Kit SDK surfaces failures as `KitError` instances with a `BorrowError`
code. Errors fall into two Borrow categories:

* **Input errors** (codes `1200`-`1299`) signal that the request is invalid,
  stale, or references state that doesn't exist. Fix the request and retry.
* **Service errors** (codes `8200`-`8299`) signal a backend or provider failure.
  Most are transient and safe to retry. A few are fatal, so check the table row
  before retrying.

## Handling errors

The thrown `KitError.name` is prefixed with `BORROW_` (for example,
`BORROW_LOAN_NOT_FOUND` for the `LOAN_NOT_FOUND` code in the input errors
table). Match on `error.code`, not on the raw name.

Use the `isInputError` and `isRetryableError` helpers to branch:

```typescript TypeScript theme={null}
import {
  BorrowError,
  isInputError,
  isRetryableError,
} from "@circle-fin/borrow-kit";

try {
  await kit.borrow.borrow(params);
} catch (error) {
  if (isInputError(error)) {
    if (error.code === BorrowError.IDEMPOTENCY_ALREADY_EXECUTED) {
      // The first submission already settled onchain. Treat as success.
    } else {
      // Fix the request: unknown market, standing authorization, etc.
    }
  } else if (isRetryableError(error)) {
    if (error.code === BorrowError.SIGNED_BUNDLE_EXPIRED) {
      // Signed execution expired. Resubmit with a new idempotencyKey.
    } else {
      await kit.borrow.retry(error);
    }
  } else {
    throw error;
  }
}
```

## Input errors

| Code | Name | When it fires |
| :- | :- | :- |
| 1200 | `LOAN_NOT_FOUND` | `loanId` does not exist. A loan that has been wound down is still readable with `status: "closed"`. Writes against it raise `LOAN_NOT_OPEN` (1223). |
| 1201 | `INVALID_OWNER_SIGNATURE` | The owner signature is missing, expired, invalid, or does not bind the exact request. |
| 1203 | `INVALID_WEBHOOK_URL` | The webhook URL is missing, invalid, or fails a reachability probe against your endpoint. |
| 1204 | `INVALID_INPUT` | General validation failure with no more specific code. |
| 1205 | `UNSUPPORTED_CHAIN` | The blockchain is not supported for Borrow. |
| 1206 | `CONFIG_NOT_FOUND` | The caller has no integrator configuration yet. Expected before the first `setIntegratorConfig` call. |
| 1207 | `MARKET_NOT_FOUND` | The requested market does not exist, is not curated, or is disabled. |
| 1208 | `STANDING_AUTHORIZATION` | A previous authorization for this owner is still active on the underlying lending protocol. The kit grants and revokes authorization within a single batch and cannot borrow through a pre-existing standing grant. Revoke it on the underlying protocol before retrying. |
| 1210 | `IDEMPOTENCY_MISMATCH` | An [`idempotencyKey`](/app-kit/howtos/borrow/use-idempotency-keys) was reused with different request parameters. |
| 1211 | `WOULD_BE_UNHEALTHY` | A collateral withdrawal would leave the loan below `1.0` health. Only raised by `withdrawCollateralRepayIfNeeded`. |
| 1212 | `ALREADY_UNHEALTHY` | A collateral withdrawal was requested against a loan that is already liquidatable. Only raised by `withdrawCollateralRepayIfNeeded`. |
| 1213 | `SIGNED_BUNDLE_EXPIRED` | An [`idempotencyKey`](/app-kit/howtos/borrow/use-idempotency-keys) was replayed after its signed execution's deadline. Retry with a new key. |
| 1214 | `LOAN_ALREADY_EXISTS` | A request to open a new loan named a market where the wallet already has one, or an earlier signed origination for the same wallet and market has not yet expired. Grow the existing loan by passing its `loanId`, or wait for the retry-after instant in the message. |
| 1215 | `IDEMPOTENCY_ALREADY_EXECUTED` | An [`idempotencyKey`](/app-kit/howtos/borrow/use-idempotency-keys) was replayed after its execution already settled onchain. Resolve the outcome from the prior execution rather than retrying. |
| 1216 | `WITHDRAWAL_EXCEEDS_COLLATERAL` | A collateral withdrawal asked for more than the loan has posted. Read the position's `collateral` and withdraw at most that, or close the loan to release all of it. |
| 1217 | `REPAY_EXCEEDS_DEBT` | A repayment asked for more than the loan owes. Read the position's `borrowed` amount and repay at most that, or use `closeLoan` to settle the debt in full. |
| 1218 | `INVALID_PAGE_CURSOR` | A `getLoans` `pageAfter` cursor fails to decode. Fetch a fresh first page rather than retrying the same cursor. |
| 1219 | `MAX_LOANS_EXCEEDED` | Opening this loan would exceed the configured maximum number of active loans, per wallet or in total. |
| 1220 | `INVALID_MARKET_SNAPSHOT` | The market's onchain data snapshot is not in a valid state for this calculation (missing oracle price, LLTV, or borrow totals). |
| 1221 | `INVALID_INTEGRATOR_FEE` | The requested integrator fee rate is outside the permitted range (`0`–`10000` bps). |
| 1222 | `UNSUPPORTED_EXISTING_LOAN` | The wallet's existing onchain loan in this market cannot have its prior history read on this blockchain, so opening it here is refused outright. Retrying will not help. |
| 1223 | `LOAN_NOT_OPEN` | The loan carries no debt, either because it was repaid or because it never borrowed. Borrow against it to open it before repaying, closing, or adding collateral. |
| 1224 | `INSUFFICIENT_COLLATERAL` | The wallet holds less collateral than the borrow needs. Post more collateral, or reduce the requested borrow amount, before retrying. |
| 1225 | `WEBHOOK_DEADLINE_OUT_OF_RANGE` | A `registerWebhook` signature's `deadline` has already passed or is beyond the service's accepted window. The signature itself may be valid. Reissue with a deadline inside the window. |

## Service errors

| Code | Name | When it fires |
| :- | :- | :- |
| 8200 | `INTERNAL_ERROR` | Internal service error. Retryable in most cases. Fatal when raised by `exploreMarketsIterator` pagination guards. |
| 8201 | `UNRECOGNIZED_RESPONSE_CHAIN` | The service returned a blockchain this SDK version cannot map. Update the SDK. Not retryable, surfaced as fatal. |
| 8202 | `REWARDS_FETCH_FAILED` | Failed to fetch reward data. Retryable. |
| 8203 | `LOAN_DATA_PENDING` | Required loan data is still being repaired. Retry shortly. |
| 8204 | `UPSTREAM_UNAVAILABLE` | An SDK dependency is temporarily unavailable. Retry shortly. |

## Other errors

Some Borrow failures surface with codes outside the `BorrowError` range:

| Code | Name | When it fires |
| :- | :- | :- |
| 1098 | `INPUT_VALIDATION_FAILED` | Client-side validation on the request. |
| 7001 | `RATE_LIMIT_EXCEEDED` | Request rate limits. |
| 9001 | `BALANCE_INSUFFICIENT_TOKEN` | The wallet holds less of a required token than the operation needs. |

## Retry a failed operation

`kit.borrow.borrow`, `kit.borrow.repay`, `kit.borrow.addCollateral`,
`kit.borrow.withdrawCollateralRepayIfNeeded`, and `kit.borrow.closeLoan` run
through several phases before they confirm onchain, and
`kit.borrow.retry(error)` resumes a failed operation from where it stopped. When
the Circle-signed execution has not expired, retry submits it again instead of
requesting a new one. An operation that already confirmed is refused, so a retry
cannot accidentally open a second loan or pay a repayment twice.

```typescript TypeScript theme={null}
try {
  await kit.borrow.borrow(params);
} catch (error) {
  if (
    isRetryableError(error) &&
    error.code !== BorrowError.SIGNED_BUNDLE_EXPIRED
  ) {
    const result = await kit.borrow.retry(error);
  }
}
```

`SIGNED_BUNDLE_EXPIRED` (1213) is marked retryable, but `kit.borrow.retry`
resubmits the same signed execution and fails again. Handle it by calling the
original operation with a new `idempotencyKey` instead.

To deduplicate submissions across process boundaries, see
[Use idempotency keys](/app-kit/howtos/borrow/use-idempotency-keys).
