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

# Errors

> The RFC 9457 Problem Details envelope used by Blank API v2, plus the stable asynchronous failure-code catalogues.

# Errors

Every Blank API v2 error is an [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) Problem Details document served as `application/problem+json`.

```json theme={null}
{
  "type": "https://blank.build/docs/reference/errors#validation_failed",
  "title": "Validation failed",
  "status": 422,
  "detail": "One or more request fields are invalid.",
  "instance": "/api/v2/predictions",
  "code": "validation_failed",
  "requestId": "req_0123456789abcdef0123456789abcdef",
  "errors": [
    {
      "path": "body.predictedPriceInSol",
      "code": "invalid_format",
      "message": "Expected a decimal string."
    }
  ]
}
```

| Field       | Type    | Always present | Meaning                                                     |
| ----------- | ------- | -------------- | ----------------------------------------------------------- |
| `type`      | URI     | Yes            | `https://blank.build/docs/reference/errors#{code}`          |
| `title`     | string  | Yes            | Short, stable, human-readable summary                       |
| `status`    | integer | Yes            | Matches the HTTP status                                     |
| `detail`    | string  | Yes            | Explanation of this occurrence                              |
| `instance`  | string  | Yes            | The request path                                            |
| `code`      | string  | Yes            | **The stable machine-readable identifier — branch on this** |
| `requestId` | string  | Yes            | `req_` plus 32 hex characters, matching `X-Request-Id`      |
| `errors`    | array   | No             | Up to 100 field errors on validation failures               |
| `context`   | object  | No             | Safe, typed facts about this occurrence                     |
| `causes`    | array   | No             | Other applicable failures discovered during the same check  |

<Warning>
  Branch on `code`, never on `detail` or `title`. `detail` is written for humans
  and may be reworded. `code` is part of the contract.
</Warning>

Error responses are always `Cache-Control: private, no-store`.

## Handling errors with the SDK

```ts theme={null}
import { BlankApiError, BlankNetworkError } from "@blankdotbuild/sdk";

try {
  await blank.predictions.create(input, { idempotencyKey });
} catch (error) {
  if (error instanceof BlankApiError) {
    console.error(error.code, error.status, error.requestId);
    console.error(error.fieldErrors); // problem.errors
    console.error(error.retryAfterSeconds);
    console.error(error.rateLimit); // { limit, remaining, resetAt, policy }
    console.error(error.idempotencyKey); // safe to retry with this key
    console.error(error.problem); // the full Problem Details document
  } else if (error instanceof BlankNetworkError) {
    // "request_aborted" | "request_timeout" | "network_error"
    console.error(error.code, error.idempotencyKey);
  }
  throw error;
}
```

`BlankNetworkError` means the outcome is **unknown**, not that the request failed. If it carries an `idempotencyKey`, retry the same request with that key rather than assuming anything.

The SDK also raises `BlankNetworkError` when Blank returns a success body that violates the operation's contract schema, so malformed data never reaches your code. Error responses are checked against the exact operation, HTTP status, code, canonical title, and type URI. If Blank or an intermediary returns anything else, the SDK synthesises `invalid_error_response` and uses the actual HTTP response status.

## Retry guidance by status

| Status                                                | Retry?     | Notes                                                                                                   |
| ----------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------- |
| 400, 401, 403, 404, 405, 409, 412, 413, 415, 422, 428 | No         | Fix the request. `409 idempotency_request_in_progress` is the one exception — retry after `Retry-After` |
| 429                                                   | Yes        | Wait for `Retry-After`                                                                                  |
| 500                                                   | Cautiously | Retry idempotent reads; retry mutations only with the same idempotency key                              |
| 502, 503, 504                                         | Yes        | Transient; back off with jitter                                                                         |

## Common codes

These can be returned by any operation.

| Status | Code                                                                 | Meaning                                                                 | Fix                                                                               |
| ------ | -------------------------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| 401    | <span id="invalid_api_key" />`invalid_api_key`                       | Missing, malformed, unknown, expired, revoked, or wrong-environment key | Check the prefix and environment, then rotate                                     |
| 403    | <span id="insufficient_scope" />`insufficient_scope`                 | The key lacks a required scope                                          | Create a new key with the scope; scopes cannot be widened                         |
| 404    | <span id="not_found" />`not_found`                                   | No route matched the request                                            | Check the method and path against the [HTTP reference](/docs/reference/http-reference) |
| 405    | <span id="method_not_allowed" />`method_not_allowed`                 | The path exists but not for this HTTP method                            | Use a method listed in the `Allow` response header                                |
| 422    | <span id="validation_failed" />`validation_failed`                   | Request failed schema validation                                        | Read `errors[].path` and `errors[].code`                                          |
| 429    | <span id="rate_limit_exceeded" />`rate_limit_exceeded`               | Rate policy exceeded                                                    | Honour `Retry-After` and the rate-limit headers                                   |
| 500    | <span id="internal_error" />`internal_error`                         | Unexpected server failure                                               | Retry if safe; report the `requestId`                                             |
| 503    | <span id="api_dependency_unavailable" />`api_dependency_unavailable` | A backing dependency is unavailable                                     | Retry with the same idempotency key                                               |
| 503    | <span id="rate_limit_unavailable" />`rate_limit_unavailable`         | The rate limiter could not be reached                                   | Retryable; requests fail closed                                                   |

One further code is produced by the SDK rather than the API. If a response cannot be
parsed as a Problem Details document, the SDK synthesises one so your error handling
still receives a consistent shape:

| Status             | Code                                                         | Meaning                                                        | Fix                                                                   |
| ------------------ | ------------------------------------------------------------ | -------------------------------------------------------------- | --------------------------------------------------------------------- |
| Actual HTTP status | <span id="invalid_error_response" />`invalid_error_response` | Blank returned an error body that is not valid Problem Details | Usually a proxy or gateway rewriting the response; report `requestId` |

## Mutation codes

Returned by any operation that writes.

| Status | Code                                                                           | Meaning                                                    | Fix                                              |
| ------ | ------------------------------------------------------------------------------ | ---------------------------------------------------------- | ------------------------------------------------ |
| 400    | <span id="malformed_json" />`malformed_json`                                   | Body is not valid JSON                                     | Send well-formed JSON                            |
| 415    | <span id="unsupported_content_type" />`unsupported_content_type`               | Missing or non-JSON `Content-Type`                         | Send `Content-Type: application/json`            |
| 400    | <span id="idempotency_key_invalid" />`idempotency_key_invalid`                 | Key is missing or outside 8–128 printable ASCII characters | Send a valid `Idempotency-Key`                   |
| 409    | <span id="idempotency_key_reused" />`idempotency_key_reused`                   | The same key was used with a different request             | Use a new key, or resend the identical request   |
| 409    | <span id="idempotency_request_in_progress" />`idempotency_request_in_progress` | The first attempt is still running                         | Wait for `Retry-After: 1` and retry the same key |
| 412    | <span id="precondition_failed" />`precondition_failed`                         | `If-Match` did not match the resource version              | Re-read the resource and retry                   |
| 413    | <span id="request_body_too_large" />`request_body_too_large`                   | Body exceeds 1,048,576 bytes                               | Send a smaller body                              |
| 400    | <span id="precondition_invalid" />`precondition_invalid`                       | `If-Match` is not one strong positive-integer ETag         | Send the exact ETag returned by the latest read  |
| 428    | <span id="precondition_required" />`precondition_required`                     | A versioned mutation omitted `If-Match`                    | Read the resource, then send its current ETag    |

## Pagination and caching

| Status | Code                                         | Meaning                                                                 | Fix                                                              |
| ------ | -------------------------------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------- |
| 400    | <span id="cursor_invalid" />`cursor_invalid` | Cursor is malformed, tampered with, or does not match the current query | Restart the traversal without a cursor and keep the query stable |

## Tokens and market data

| Status | Code                                                               | Fix                                                           |
| ------ | ------------------------------------------------------------------ | ------------------------------------------------------------- |
| 404    | <span id="token_not_found" />`token_not_found`                     | Check the mint address; the token may not be publicly visible |
| 404    | <span id="holder_stats_not_found" />`holder_stats_not_found`       | No indexed holder snapshot exists yet                         |
| 422    | <span id="market_time_range_invalid" />`market_time_range_invalid` | Send a valid `from`/`to` window for the requested interval    |
| 503    | <span id="token_index_unavailable" />`token_index_unavailable`     | Transient indexer unavailability; retry                       |
| 503    | <span id="market_data_unavailable" />`market_data_unavailable`     | Transient market-data unavailability; retry                   |

## Identity, staking, fees, and presales

| Status | Code                                                                           | Fix                                                                   |
| ------ | ------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
| 503    | <span id="identity_unavailable" />`identity_unavailable`                       | Retry `GET /me`                                                       |
| 404    | <span id="staking_pool_not_found" />`staking_pool_not_found`                   | Staking is not enabled for this token, or the pool is not indexed yet |
| 404    | <span id="presale_not_found" />`presale_not_found`                             | Check the presale ID                                                  |
| 404    | <span id="presale_participation_not_found" />`presale_participation_not_found` | The key's pinned wallet has no participation in this presale          |
| 503    | <span id="manifest_unavailable" />`manifest_unavailable`                       | Retry the Solana manifest read                                        |

## Price predictions

Prediction eligibility evaluates every independent requirement, including finalized token balance even when the round is closed. The eligibility response can therefore contain both `round_not_open` and `insufficient_balance`. A submission returns one primary Problem Details code and places any other failures found during the same evaluation in `causes[]`; each item has its canonical `code`, `title`, `detail`, and optional `context`.

| Status    | Code                                                                             | Fix                                                                   |
| --------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| 403       | <span id="prediction_wallet_mismatch" />`prediction_wallet_mismatch`             | `walletAddress` must equal the key's pinned wallet                    |
| 403       | <span id="prediction_authorization_invalid" />`prediction_authorization_invalid` | Sign the exact stored delegated intent with its wallet                |
| 404       | <span id="prediction_intent_not_found" />`prediction_intent_not_found`           | Check the intent ID and use the same integration API key              |
| 404       | <span id="prediction_round_not_found" />`prediction_round_not_found`             | Check the mint and round ID                                           |
| 404       | <span id="prediction_not_found" />`prediction_not_found`                         | The prediction does not exist or belongs to another account           |
| 404 / 409 | <span id="prediction_token_not_enabled" />`prediction_token_not_enabled`         | The token has no Price Prediction allocation; this is fixed at launch |
| 409       | <span id="prediction_round_not_open" />`prediction_round_not_open`               | Submit while the round is `open`, before `locksAt`                    |
| 409       | <span id="prediction_wallet_not_verified" />`prediction_wallet_not_verified`     | Verify the wallet on the Blank account                                |
| 409       | <span id="prediction_username_required" />`prediction_username_required`         | Set a username on the Blank account                                   |
| 409       | <span id="prediction_insufficient_balance" />`prediction_insufficient_balance`   | The wallet does not hold enough of the token at the evaluated slot    |
| 409       | <span id="prediction_already_submitted" />`prediction_already_submitted`         | One prediction per wallet per round; predictions are immutable        |
| 409       | <span id="prediction_intent_expired" />`prediction_intent_expired`               | Prepare and sign a new delegated intent while the round is open       |
| 409       | <span id="prediction_intent_consumed" />`prediction_intent_consumed`             | Replay with the original idempotency key or prepare a new intent      |
| 409       | <span id="prediction_exact_values_locked" />`prediction_exact_values_locked`     | Standings are withheld until the round durably locks                  |
| 422       | <span id="prediction_price_out_of_range" />`prediction_price_out_of_range`       | Positive decimal, at most 12 integer and 18 fractional digits         |
| 503       | <span id="prediction_balance_unavailable" />`prediction_balance_unavailable`     | Finalized balance data is temporarily unreadable; retry               |

## Transaction-intent result codes

These codes appear in `TransactionIntent.error.code` after asynchronous Solana processing. They are not HTTP Problem Details codes.

| Code                                     | Meaning                                                        | Next action                                       |
| ---------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------- |
| `solana_transaction_failed`              | Solana reported that the submitted transaction failed          | Prepare a fresh intent after correcting the cause |
| `transaction_intent_invalid_state`       | Stored intent state could not be processed safely              | Report the intent ID and request ID to Blank      |
| `transaction_intent_expired`             | Confirmation was not established before the intent expired     | Prepare and sign a fresh intent                   |
| `transaction_intent_broadcast_exhausted` | Delivery retries ended before broadcast could complete         | Read the intent, then prepare a fresh one         |
| `solana_blockhash_expired`               | The transaction blockhash expired before confirmation          | Prepare and sign a fresh intent                   |
| `solana_signature_mismatch`              | The RPC returned a signature different from the signed payload | Report the intent ID; do not resubmit blindly     |

## Operations and transaction intents

| Status | Code                                                                                     | Fix                                                      |
| ------ | ---------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| 404    | <span id="operation_not_found" />`operation_not_found`                                   | Unknown operation, or owned by another account           |
| 404    | <span id="transaction_intent_not_found" />`transaction_intent_not_found`                 | Unknown intent, or owned by another account              |
| 409    | <span id="transaction_intent_not_prepared" />`transaction_intent_not_prepared`           | The intent is no longer awaiting a signature             |
| 409    | <span id="transaction_intent_expired" />`transaction_intent_expired`                     | The blockhash window closed; request a fresh intent      |
| 412    | <span id="transaction_intent_version_conflict" />`transaction_intent_version_conflict`   | Re-read the intent and resubmit with its current version |
| 422    | <span id="transaction_intent_tampered" />`transaction_intent_tampered`                   | Sign the prepared message byte-for-byte                  |
| 422    | <span id="transaction_intent_wrong_signer" />`transaction_intent_wrong_signer`           | Sign with the wallet in `expectedSigner`                 |
| 422    | <span id="transaction_intent_invalid_signature" />`transaction_intent_invalid_signature` | The signature failed verification; re-sign               |

## Webhooks

| Status | Code                                                                                       | Fix                                                                                       |
| ------ | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| 404    | <span id="webhook_endpoint_not_found" />`webhook_endpoint_not_found`                       | Unknown endpoint, or owned by another account                                             |
| 404    | <span id="webhook_delivery_not_found" />`webhook_delivery_not_found`                       | Unknown delivery, or owned by another account                                             |
| 409    | <span id="webhook_delivery_not_replayable" />`webhook_delivery_not_replayable`             | The delivery is not in a replayable state                                                 |
| 409    | <span id="webhook_endpoint_limit_reached" />`webhook_endpoint_limit_reached`               | An account may hold at most 20 endpoints; delete one before creating another              |
| 409    | <span id="webhook_delivery_replay_limit_reached" />`webhook_delivery_replay_limit_reached` | A delivery may be replayed at most 10 times                                               |
| 422    | <span id="webhook_url_disallowed" />`webhook_url_disallowed`                               | Use a public HTTPS URL that satisfies the [endpoint URL policy](/docs/for-developers/webhooks) |

### Webhook delivery result codes

`WebhookDelivery.lastErrorCode` is one of the following transport codes, or `http_<status>` for an HTTP rejection such as `http_500`:

| Code                        | Meaning                                      |
| --------------------------- | -------------------------------------------- |
| `request_timeout`           | The 10-second delivery deadline elapsed      |
| `network_error`             | The destination could not be reached         |
| `url_disallowed`            | URL or DNS policy rejected the destination   |
| `redirect_limit_exceeded`   | More than three redirects were returned      |
| `redirect_location_missing` | A redirect response omitted `Location`       |
| `http_<status>`             | The destination returned a non-2xx HTTP code |

## Reporting a problem

Include the `requestId` and the `code`. They identify the exact request in Blank's telemetry. Never paste an API key, a webhook signing secret, or a signed transaction into a report.

For symptom-first guidance, see [Troubleshooting](/docs/reference/troubleshooting).
