> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Solana gas sponsorship errors

Errors from the Solana RPC endpoint are returned as JSON-RPC error responses with a numeric
`code`, a human-readable `message`, and an optional `data` object.

Openfort's own errors use the codes below. Errors raised further down the stack — by Kora, by
Jito, or by the Solana RPC node — are passed through with their original code and message, so
you may see codes that are not listed here.

## Policy errors

Sponsorship requires a fee sponsorship whose policy rules accept the transaction. Policies are
normally discovered automatically from the project's enabled, project-scoped fee sponsorships;
you can also pass an explicit `policyId` in the request params to select one policy.

How a criteria mismatch is reported depends on the **policy's scope**, not on whether you passed
`policyId`: a `transaction`-scoped policy fails loudly with [`POLICY_REJECTED`](#policy_rejected),
while `project`- and `account`-scoped policies fail softly and are skipped. See
[Solana policy rules](https://www.openfort.io/docs/configuration/policies/solana-rules).

### NO\_POLICY\_FOUND

```json
{
  "code": -32006,
  "message": "No matching Solana fee sponsorship policy found. A valid policy is required for sponsorship."
}
```

No `policyId` was supplied, and none of the project's project-scoped fee sponsorships accepted
the transaction. This covers both "the project has no sponsorship policy" and "it has one, but
its rules did not match" — auto-discovery cannot tell you which policy failed or why.

**Solutions:**

* Confirm the project has an **enabled** fee sponsorship whose policy is **project**-scoped with an `accept` rule for the `sponsorSolTransaction` operation.
* Review the rule's criteria against the transaction you actually send. Criteria are [fail-closed](https://www.openfort.io/docs/configuration/policies/solana-rules#criteria): a criterion reading a field the transaction does not carry (`solValue` on an SPL-only transfer, for example) rejects it, and a `programId` list must name **every** program the transaction invokes, including ComputeBudget. Clearing the criteria makes the rule match unconditionally, which isolates whether criteria are the cause.
* For a rule you want to fail loudly, give the policy `transaction` scope and pass its fee sponsorship id (`pol_…`) as `policyId`. That path reports [`POLICY_REJECTED`](#policy_rejected) with the reason instead of this error. Only pass a `transaction`-scoped policy as `policyId`; a criteria mismatch on a `project`- or `account`-scoped policy named this way is not reported.
* Confirm the key's mode matches the cluster. A test key reaches `devnet` and a live key reaches `mainnet-beta`; policies live in the matching environment.

:::warning
`POST /v2/policies/evaluate` is **not** a valid pre-flight check for sponsorship. It deliberately
excludes fee-sponsorship-linked policies, so it reports `"No policies configured, defaulting to
allow"` even when a working sponsorship policy exists. Use `policyId` on the RPC request instead.
:::

### POLICY\_REJECTED

```json
{
  "code": -32006,
  "message": "Policy violation for '3f760d50-c3d3-4b2e-8e7c-4f39d9c42b38': No matching Solana rule",
  "data": {
    "policyUuid": "3f760d50-c3d3-4b2e-8e7c-4f39d9c42b38",
    "reason": "No matching Solana rule"
  }
}
```

A `transaction`-scoped policy was named by `policyId` and the transaction did not satisfy its
rules. The uuid is unprefixed here, unlike the `pol_…` id you send. `reason` is
`No matching Solana rule` when no rule matched, and `Matched reject rule` when a `reject` rule
matched first.

**Solutions:**

* Compare the transaction against the policy's criteria — recipients, amounts, mint, and program IDs.
* Check for a `reject` rule ahead of your `accept` rule; rules are evaluated in order and the first match wins.

### Policy is not active

```json
{
  "code": -32602,
  "message": "Invalid parameters: Policy is not active: pol_3f760d50-c3d3-4b2e-8e7c-4f39d9c42b38"
}
```

The `policyId` names a fee sponsorship that exists but is disabled.

**Solutions:**

* Enable the fee sponsorship in the [Dashboard](https://dashboard.openfort.io).

### Malformed policy id

```json
{
  "code": -32602,
  "message": "Invalid parameters: Invalid policyId: must be in format pol_<uuid> at \"policyId\""
}
```

`policyId` takes the **fee sponsorship** id, prefixed `pol_`. The policy configuration (`ply_…`)
and rule (`plr_…`) ids are different objects and are rejected here.

:::warning
A well-formed `pol_…` id that does not exist in the project environment currently returns
`{"code": -32603, "message": "Internal error"}` rather than a not-found error. If a request
carrying `policyId` returns that, check the id and that your API key belongs to the same project
**and mode**.
:::

***

## Validation errors

### Invalid transaction

```json
{
  "code": -32602,
  "message": "Invalid transaction: Failed to decode transaction for policy validation: ..."
}
```

The transaction could not be base64-decoded or deserialized.

**Solutions:**

* Verify the transaction is base64-encoded wire format, not base58 and not a bare message.
* Check that you are using a compatible version of `@solana/kit` or `@solana/web3.js`.

### Invalid cluster

```json
{
  "code": -32602,
  "message": "Invalid cluster: testnet. Must be 'mainnet-beta' or 'devnet'."
}
```

Only `devnet` and `mainnet-beta` are supported in the endpoint URL.

### Cluster does not match the API key mode

```json
{
  "code": -32602,
  "message": "Request has invalid parameters. You're using a test API key but you provided chainId 1."
}
```

Test-mode keys can only reach `devnet`; live-mode keys can only reach `mainnet-beta`. The chain id
in the message is Openfort's SVM chain id — `1` for `mainnet-beta` and `2` for `devnet` — not
Solana's cluster numbering.

**Solutions:**

* Use the publishable key for the mode matching the cluster in your endpoint URL.

### Invalid parameters

```json
{
  "code": -32602,
  "message": "Invalid parameters: ..."
}
```

A request parameter failed schema validation. The message names the offending field.

***

## Signing and broadcast errors

Openfort does not raise its own signing or broadcast errors. Once the policy, mode and balance
gates pass, the request goes to Kora, and a Kora or Solana RPC failure is returned with **its**
code and message unchanged. Read the message rather than matching on a code.

One case is rewritten before it reaches you, because the retry advice differs:

### Transaction broadcast but not confirmed

```json
{
  "code": -32003,
  "message": "Transaction was broadcast but not confirmed before its blockhash expired. It may still land on-chain — check the signature status before retrying."
}
```

Kora's `-32093` "unable to confirm transaction" is surfaced as `-32003`. The transaction was sent.

**Solutions:**

* **Check the signature status before retrying** — the transaction may still confirm, and a blind retry can send it twice.
* Fetch a fresh blockhash immediately before building the transaction; Solana blockhashes expire in roughly 60 seconds.

***

## Balance errors

### Insufficient sponsorship balance

```json
{
  "code": -32602,
  "message": "Insufficient Openfort sponsorship balance to cover this transaction's gas fees. Add funds to your account to continue sponsoring transactions."
}
```

The project's available credit does not cover the projected fee. This gate runs **after** policy
resolution, so it never presents as a policy error.

**Solutions:**

* Top up in the [Dashboard](https://dashboard.openfort.io) under Settings → Billing.

SPL fee payment is settled by Kora rather than against your Openfort balance, so an unsupported
mint or a payer whose token balance does not cover the fee is rejected by Kora and returned with
Kora's own code and message. Call
[`getSupportedTokens`](https://www.openfort.io/docs/products/infrastructure/paymaster/solana/endpoints#getsupportedtokens)
for the mints the endpoint accepts.

***

## Rate limiting

### Rate limited

```json
{
  "code": -32029,
  "message": "Rate limit exceeded",
  "data": { "retryAfter": 60 }
}
```

**Solutions:**

* Wait `retryAfter` seconds, then retry with exponential backoff.
* Consider upgrading your subscription for higher limits.

***

## Infrastructure errors

### Sponsorship billing failed

```json
{
  "code": -32603,
  "message": "Failed to reserve sponsorship balance for this transaction. The transaction was not signed — please retry."
}
```

The sponsorship record could not be reserved. **The transaction was not signed or broadcast**, so
retrying is safe.

### Service unavailable

```json
{
  "code": -32603,
  "message": "Kora service unavailable for cluster devnet",
  "data": { "cluster": "devnet" }
}
```

```json
{
  "code": -32603,
  "message": "Kora request timed out"
}
```

**Solutions:**

* Check the [Openfort Status Page](https://status.openfort.io), retry after a brief delay, and contact support if it persists.

***

## Jito bundle errors

These errors are specific to the [`estimateBundleFee`](https://www.openfort.io/docs/products/infrastructure/paymaster/solana/endpoints#estimatebundlefee) and [`signAndSendBundle`](https://www.openfort.io/docs/products/infrastructure/paymaster/solana/endpoints#signandsendbundle) bundle methods. Bundles are **mainnet-only**.

### Duplicate transactions

```json
{
  "code": -32060,
  "message": "Jito error: Jito API error: duplicate transactions"
}
```

Two transactions in the bundle are byte-identical. Jito rejects bundles with duplicate transactions.

**Solutions:**

* Make each transaction unique — add a per-transaction memo or vary the instructions so no two encode to the same bytes.

### Insufficient bundle payment

```json
{
  "code": -32000,
  "message": "InsufficientBundlePayment"
}
```

The SPL payment in the bundle doesn't cover the Kora fee **plus** the Jito tip.

**Solutions:**

* Remember that [`estimateBundleFee`](https://www.openfort.io/docs/products/infrastructure/paymaster/solana/endpoints#estimatebundlefee) **excludes the tip**. Convert the tip to token units (`fee_in_token / fee_in_lamports × tipLamports`), add it to the estimated fee, and apply a small buffer before setting the payment amount.

### Invalid or expired blockhash

```json
{
  "code": -32093,
  "message": "RPC error: Custom: Invalid blockhash"
}
```

The bundle's blockhash was rejected at simulation time — usually because it was fetched too recently for the simulating node to have seen it yet, or it has since expired.

**Solutions:**

* Fetch the blockhash immediately before building the bundle, and retry with a fresh blockhash on this error (Jito's recommended resend behavior).

### Too many transactions

```json
{
  "code": -32602,
  "message": "Invalid parameters: A bundle must contain at most 5 transactions at \"transactions\""
}
```

A bundle carries 1–5 transactions.

**Solutions:**

* Split the work across multiple bundles, or reduce the transaction count to 5 or fewer.

### No SPL payment policy

```json
{
  "code": -32602,
  "message": "Invalid parameters: Bundles require an SPL payment policy"
}
```

Bundles are sponsored only by a fee sponsorship that charges the user in an SPL token. The project
has no enabled project-scoped SPL fee sponsorship, or the `policyId` you passed names one that
doesn't charge in an SPL token.

**Solutions:**

* Create an [SPL fee sponsorship](https://www.openfort.io/docs/configuration/gas-spl), or pass the `pol_…` id of one as `policyId`.

### Mixed blockhashes

```json
{
  "code": -32602,
  "message": "Invalid parameters: All bundle transactions must use the same blockhash"
}
```

The transactions in the bundle were built with different blockhashes.

**Solutions:**

* Fetch one blockhash and use it for every transaction in the bundle.

***

## Debugging tips

1. **Narrow a -32006 by removing criteria**: auto-discovery cannot report which policy failed or why. Clear the rule's criteria to confirm the policy is reachable at all, then add them back one at a time. A `transaction`-scoped policy passed as `policyId` reports the reason directly.

2. **Verify cluster and mode consistency**: Your key's mode, the cluster in the endpoint URL, and the environment the policy lives in must all agree — test key with `devnet`, live key with `mainnet-beta`.

3. **Test on devnet first**: Debug issues on devnet before deploying to mainnet-beta.

4. **Monitor your dashboard**: The Openfort Dashboard shows sponsorship activity and can help identify patterns in failures.

5. **Use fresh blockhashes**: Solana blockhashes expire quickly. Always get a fresh blockhash before requesting sponsorship.

## Getting help

If you continue to experience issues:

* Check the [Openfort Status Page](https://status.openfort.io) for service disruptions
* Review [Gasless Solana transactions](https://www.openfort.io/docs/products/embedded-wallet/react/wallet/actions/send-transaction/solana#gasless-solana-transactions) for working examples
* Contact support through the [Dashboard](https://dashboard.openfort.io)
