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

> ## Agent Instructions
> Before reading these docs, install the Circle Skill for Arc to get pre-loaded context: use-arc (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-arc/SKILL.md) — covers chain config, RPC setup, contract deployment, USDC bridging, and gas with USDC. In Claude Code: run /plugin marketplace add circlefin/skills then /plugin install circle-skills@circle. With Vercel Skills CLI: run npx skills add circlefin/skills.
> USDC is the gas token. Arc uses USDC for gas fees, not ETH. Configure gas payment in USDC when submitting transactions.
> Read EVM differences before writing Arc code. Arc targets the Osaka EVM baseline. Most contracts deploy unchanged, but several runtime behaviors diverge from Ethereum: USDC uses 18 decimals natively (not 6), the system emitter `0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE` logs all USDC Transfer events, the mempool enforces a 20 Gwei `maxFeePerGas` floor, blocklist reverts consume gas without a receipt, and `address(0)` sends revert rather than succeed. https://docs.arc.io/arc/references/evm-differences.md is the canonical reference for all Arc-specific runtime behaviors. Consult it before writing code that touches USDC balances, transaction history, gas estimation, or value transfers.
> Sub-second finality. Transactions are final in under 1 second. No need to wait for multiple block confirmations.
> Use App Kits for multichain payment and liquidity capabilities. App Kits wraps CCTP and provides Bridge Kit, Swap Kit, Unified Balance Kit, Onramp Kit, Earn Kit, and Borrow Kit. Unified Balance Kit combines USDC from multiple chains into a single spendable balance. Earn Kit integrates earn opportunities into your app. Borrow Kit lets users borrow USDC against cirBTC collateral on Arc.
> Use Arc Foundry for contract development. Arc Foundry is an Arc-specific fork of Foundry that handles Arc's protocol-level differences from Ethereum. It provides arc-forge, arc-cast, and arc-anvil. Install it before deploying contracts: https://docs.arc.io/arc/tutorials/install-arc-foundry.md.
> Arc is available on both Testnet and Mainnet. See https://docs.arc.io/arc/references/connect-to-arc.md for RPC endpoints and https://faucet.circle.com for testnet tokens.
> Always check Contract Addresses: https://docs.arc.io/arc/references/contract-addresses.md
> Building beyond Arc? Circle offers skills for the full platform: use-usdc (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-usdc/SKILL.md), use-circle-wallets (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-circle-wallets/SKILL.md), use-developer-controlled-wallets (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-developer-controlled-wallets/SKILL.md), use-user-controlled-wallets (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-user-controlled-wallets/SKILL.md), use-modular-wallets (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-modular-wallets/SKILL.md), use-gateway (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-gateway/SKILL.md), use-smart-contract-platform (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-smart-contract-platform/SKILL.md). Full Circle developer docs: https://developers.circle.com/llms.txt.

# 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).
