# Error handling

Handle failed requests, webhook events and stored errors with a Stripe SDK.

Use a Stripe SDK to catch failed API requests, handle webhook notifications and inspect stored errors. Error details help you decide whether to correct a request, ask your customer to act or investigate an uncertain outcome. Select your SDK language to see its examples and error types.

Choose your next action based on what you know about the request’s outcome.

| Situation | Next action |
| --- | --- |
| Parameters are invalid or authentication fails | Correct the [request or API key](https://docs.stripe.com/error-handling.md#error-types) before trying again. |
| A payment is declined or needs customer action | Handle the [payment error](https://docs.stripe.com/error-handling.md#payment-errors) and ask the customer to complete the required action. |
| A connection fails or a request is still executing | [Retry safely](https://docs.stripe.com/error-low-level.md#network-errors) with the same idempotency key and parameters. |
| A server error or another failure leaves the outcome unknown | [Reconcile the request](https://docs.stripe.com/error-low-level.md#server-errors) before starting another operation. |

## Before you begin

Before running the examples:

- Install a supported [Stripe SDK](https://docs.stripe.com/sdks.md) and its required runtime. For Python, use Python 3.9 or later. These examples use the SDK’s `v1` services where available.
- Use a sandbox with [test payment methods](https://docs.stripe.com/testing.md).
- Create a [restricted API key](https://docs.stripe.com/keys/restricted-api-keys.md) with only the permissions needed by the examples you run. See the [permissions reference](https://docs.stripe.com/keys/permissions-reference.md):
  - `payment_intent_write` to create `PaymentIntent` objects.
  - `payment_intent_read` to retrieve `PaymentIntent` objects.
  - `charge_read` to retrieve `Charge` objects.
- Load your key [from a secure environment variable or secrets manager](https://docs.stripe.com/keys-best-practices.md). For examples that read `STRIPE_SECRET_KEY`, set that variable to your restricted key.
- For webhook examples, set up a [webhook endpoint](https://docs.stripe.com/webhooks/quickstart.md), install the example’s web framework and set `STRIPE_WEBHOOK_SECRET` to that endpoint’s signing secret. When forwarding events with the Stripe CLI, use the signing secret from `stripe listen`.

## Read error details

When an API request fails, use the error response to identify the problem. Keep the request ID so you can find the request in your logs or share it with Stripe support. Depending on the error, the response can include the following details:

| Property | Description |
| --- | --- |
| `code` | The error code. |
| `doc_url` | A link to the Stripe documentation for the specific error code. |
| `message` | A description of the problem. |
| `param` | The request parameter associated with the error, if applicable. |
| `request_log_url` | A link to the request log in the Stripe Dashboard. |
| Request ID | A unique identifier for the request, beginning with `req_`. The response headers contain this value. The examples show how to read it from an SDK exception or error value. |
| `type` | The error category. |

Use these techniques to handle errors identified through different channels. Use the [error type](https://docs.stripe.com/error-handling.md#error-types) to choose a response.

| Technique | Where to use it |
| --- | --- |
| [Handle API request errors](https://docs.stripe.com/error-handling.md#catch-exceptions) | Exceptions or error values returned by individual API requests. |
| [Monitor webhooks](https://docs.stripe.com/error-handling.md#monitor-webhooks) | Failures reported through events, including failures that occur after an API request completes. |
| [Inspect stored errors](https://docs.stripe.com/error-handling.md#use-stored-information) | Errors or error-related information stored in objects. |

## Handle API request errors

Stripe SDKs expose failed API requests as exceptions or error values. Handle these in your application and use their details to choose a response.

For HTTP status codes, idempotency and retries, see [Advanced error handling](https://docs.stripe.com/error-low-level.md) and the [API error reference](https://docs.stripe.com/api/errors.md).

# Node.js


Use `async` and `await` with a `try` and `catch` block to handle a failed API request:

1. Declare the function that makes the request with `async`.
2. Use `await` before the API call.
3. Catch the error and use its [type](https://docs.stripe.com/error-handling.md#error-types) to choose a response.


#### Node.js

```javascript
// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
const stripe = require('stripe')('<<YOUR_SECRET_KEY>>');

async function exampleFunction(args) {
  try {
    const paymentIntent = await stripe.paymentIntents.create(args);
    console.log('No error.');
  } catch (e) {
    switch (e.type) {
      case 'StripeCardError':
        console.log(`A payment error occurred: ${e.message}`);
        break;
      case 'StripeInvalidRequestError':
        console.log('An invalid request occurred.');
        break;
      // Add additional case statements for other Stripe error types as needed.
      // See all error types at https://docs.stripe.com/api/errors/handling?lang=node
      default:
        if (e instanceof stripe.errors.StripeError) {
          // All other Stripe errors
          console.log(`Status: ${e.statusCode}, Code: ${e.code}, Message: ${e.message}, Request ID: ${e.requestId}`);
        } else {
          throw e;
        }
        break;
    }
  }
}
```

### Test API request errors

Run the following scenarios in a sandbox to test your error handler. The examples explicitly select card payments so that automatic payment method defaults don’t require a `return_url` before the intended result. Log formatting and request IDs vary by SDK.

#### Error to trigger - Invalid request

#### Node.js

```javascript
exampleFunction({
  // The required parameter currency is missing
  amount: 2000,
  confirm: true,
  payment_method_types: ['card'],
  payment_method: 'pm_card_visa', // This is a test card that always succeeds when you use it in test environments. Use it and other test cards to test error handling in your integration. In production code, you would use a real payment method here.
})
```

```
An invalid request occurred.
```

#### Error to trigger - Card error

#### Node.js

```javascript
exampleFunction({
  currency: 'usd',
  amount: 2000,
  confirm: true,
  payment_method_types: ['card'],
  payment_method: 'pm_card_chargeDeclinedFraudulent', // This is a test card that always produces a fraudulent charge payment error. Use it and other test cards to test error handling in your integration. In production code, you would use a real payment method here.
})
```

```
A payment error occurred: Your card was declined.
```

#### Error to trigger - No error

#### Node.js

```javascript
exampleFunction({
  currency: 'usd',
  amount: 2000,
  confirm: true,
  payment_method_types: ['card'],
  payment_method: 'pm_card_visa' // This is a test card that always succeeds when you use it in test environments. Use it and other test cards to test error handling in your integration. In production code, you would use a real payment method here.
})
```

```
No error.
```

## Monitor webhooks

Use *webhooks* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) to learn about failures reported through events. These can include failures that aren’t returned in an API response:

- A dispute is lost.
- A recurring payment fails.
- Your front end *confirms* (Confirming a PaymentIntent indicates that the customer intends to pay with the current or provided payment method. Upon confirmation, the PaymentIntent attempts to initiate a payment) a payment, then disconnects before receiving the result, but your server can still receive the failure event.

In your webhook handler, [verify the signature](https://docs.stripe.com/webhooks.md#verify-events) using the raw request body, the `Stripe-Signature` header and your endpoint’s signing secret before processing the event. Reject invalid payloads or signatures with a `400` response. If you use Express, as in the Node.js example, register this route before any middleware that parses JSON, because verification requires the unmodified request body.

If you restrict inbound traffic by IP address, allow Stripe’s [public webhook IP addresses](https://docs.stripe.com/ips.md).

The following examples handle snapshot events, which include the affected object in `data.object`. For thin events, follow the [thin-event processing guidance](https://docs.stripe.com/events/how-events-work.md#thin-events) to retrieve the details you need.

After verifying a snapshot event that reports a failure, inspect the affected object:

1. Access [event.data.object](https://docs.stripe.com/api/events/object.md#event_object-data-object) to retrieve the affected object.
2. Inspect the [stored error](https://docs.stripe.com/error-handling.md#use-stored-information), if the object contains one.
3. Use the [error type](https://docs.stripe.com/error-handling.md#error-types) to choose a response.

#### Node.js

```javascript
// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
const stripe = require('stripe')('<<YOUR_SECRET_KEY>>');
const express = require('express');
const app = express();
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => {

  let event;
  try {
    event = stripe.webhooks.constructEvent(
      request.body, request.headers['stripe-signature'], endpointSecret
    );
  } catch (err) {
    return response.sendStatus(400);
  }

  // Use its type to find out what happened
  if (event.type == 'payment_intent.payment_failed') {

    // Get the object affected
    const paymentIntent = event.data.object;

    // Use stored information to get an error object
    const error = paymentIntent.last_payment_error;
    if (!error) {
      return response.sendStatus(200);
    }

    // Use its type to choose a response
    switch (error.type) {
      case 'card_error':
        console.log(`A payment error occurred: ${error.message}`);
        break;
      case 'invalid_request_error':
        console.log('An invalid request occurred.');
        if (error.param) {
          console.log(`The parameter ${error.param} is invalid or missing.`);
        }
        break;
      default:
        console.log('Another problem occurred, maybe unrelated to Stripe.');
        break;
    }
  }
  response.send();
});

app.listen(4242, () => console.log('Running on port 4242'));
```

### Test webhook handling

Set up [local webhook testing](https://docs.stripe.com/webhooks.md#test-webhook), then trigger a failed payment to test your handler:

```bash
stripe trigger payment_intent.payment_failed
```

```bash
A payment error occurred: Your card was declined.
```

## Inspect stored errors

Some Stripe objects store details about a failure. Retrieve the object and inspect its error properties to investigate the problem or add context to an event. For example, a `PaymentIntent` stores the error from its previous confirmation attempt in [last_payment_error](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-last_payment_error).

An empty value doesn’t prove that the payment never failed, because Stripe clears `last_payment_error` when you update the `PaymentIntent`. This property isn’t a historical error log.

1. Retrieve the `PaymentIntent`.
2. Check whether `last_payment_error` contains an error.
3. If present, log its type and the `PaymentIntent` ID.

#### Node.js

```javascript
// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
const stripe = require('stripe')("<<YOUR_SECRET_KEY>>");

const payment_intent = await stripe.paymentIntents.retrieve({{PAYMENT_INTENT_ID}})

const e = payment_intent.last_payment_error

if (e !== null) {
  console.log(`PaymentIntent ${payment_intent.id} experienced a ${e.type} error.`)
}
```

Here are common objects that store information about failures.

| Object | Attribute | Values |
| --- | --- | --- |
| [PaymentIntent](https://docs.stripe.com/api/payment_intents.md) | `last_payment_error` | [An error object](https://docs.stripe.com/error-handling.md#parse-error-data) |
| [SetupIntent](https://docs.stripe.com/api/setup_intents.md) | `last_setup_error` | [An error object](https://docs.stripe.com/error-handling.md#parse-error-data) |
| [Invoice](https://docs.stripe.com/api/invoices.md) | `last_finalization_error` | [An error object](https://docs.stripe.com/error-handling.md#parse-error-data) |
| [SetupAttempt](https://docs.stripe.com/api/setup_attempts.md) | `setup_error` | [An error object](https://docs.stripe.com/error-handling.md#parse-error-data) |
| [Payout](https://docs.stripe.com/api/payouts.md) | `failure_code` | [A payout failure code](https://docs.stripe.com/api/payouts/failures.md) |
| [Refund](https://docs.stripe.com/api/refunds.md) | `failure_reason` | [A refund failure code](https://docs.stripe.com/api/refunds/object.md#refund_object-failure_reason) |

### Test stored errors

Simulate a failure in a sandbox, then retrieve the affected object to check its error properties:

- [Declined payment](https://docs.stripe.com/testing.md#declined-payments).
- [Failed payout](https://docs.stripe.com/connect/testing.md#account-numbers).
- [Failed refund](https://docs.stripe.com/testing.md#refunds).

## Choose a response by error type

Use the error type to choose a response. SDK exception names and raw API error types can differ. Stored errors, such as `last_payment_error`, use API type values such as `card_error` and `invalid_request_error`. Compare these values with API types, not SDK exception names.

In the Stripe Node.js library, each error object has a `type` attribute. Use the documentation for each type for advice about how to respond.

For an SDK exception, `e.type` identifies the SDK error class. When available, `e.rawType` contains the raw API error type.

| Name | SDK exception | Description |
| --- | --- | --- |
| Payment error | [StripeCardError](https://docs.stripe.com/error-handling.md#payment-errors) | An error occurred during a payment, involving one of these situations:
- [Blocked payments](https://docs.stripe.com/error-handling.md#payment-blocked)
- [Issuer declines](https://docs.stripe.com/error-handling.md#payment-declined).
- [Other payment errors](https://docs.stripe.com/error-handling.md#other-payment-errors). |
| Invalid request error | [StripeInvalidRequestError](https://docs.stripe.com/error-handling.md#invalid-request-errors) | You made an API call with the wrong parameters, in the wrong state, or in an invalid way. |
| Connection error | [StripeConnectionError](https://docs.stripe.com/error-handling.md#connection-errors) | There was a network problem between your server and Stripe. |
| API error | [StripeAPIError](https://docs.stripe.com/error-handling.md#api-errors) | Something went wrong on Stripe’s end. (These are rare.) |
| Authentication error | [StripeAuthenticationError](https://docs.stripe.com/error-handling.md#authentication-errors) | Stripe can’t authenticate you with the information provided. |
| Idempotency error | [StripeIdempotencyError](https://docs.stripe.com/error-handling.md#idempotency-errors) | You used an [idempotency key](https://docs.stripe.com/api/idempotent_requests.md) for something unexpected, like replaying a request but passing different parameters. |
| Permission error | [StripePermissionError](https://docs.stripe.com/error-handling.md#permission-errors) | The API key used for this request doesn’t have the necessary permissions. |
| Rate limit error | [StripeRateLimitError](https://docs.stripe.com/error-handling.md#rate-limit-errors) | You made too many API calls in too short a time. |
| Signature verification error | [StripeSignatureVerificationError](https://docs.stripe.com/error-handling.md#signature-verification-errors) | You’re using *webhook* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) [signature verification](https://docs.stripe.com/webhooks.md#verify-events) and couldn’t verify that a webhook event is authentic. |

## Payment errors

Payment errors can occur with any payment method, even when the SDK calls them card errors. They fall into three categories:

- [Blocked payments](https://docs.stripe.com/error-handling.md#payment-blocked)
- [Issuer declines](https://docs.stripe.com/error-handling.md#payment-declined)
- [Other payment errors](https://docs.stripe.com/error-handling.md#other-payment-errors)

To choose a response, check the [error code](https://docs.stripe.com/error-codes.md), [decline code](https://docs.stripe.com/declines/codes.md) and [charge outcome](https://docs.stripe.com/api/charges/object.md#charge_object-outcome). To identify the correct charge, check whether the error contains a [PaymentIntent](https://docs.stripe.com/api/errors.md#errors-payment_intent) with a [latest Charge](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-latest_charge), then retrieve that `Charge`.

If the charge is absent or the lookup fails, use the original payment error code. A missing charge or failed lookup doesn’t necessarily mean that Radar blocked the payment.

Use a [restricted API key](https://docs.stripe.com/keys/restricted-api-keys.md) with `payment_intent_write` to create `PaymentIntent` objects and `charge_read` to retrieve `Charge` objects. See the [permissions reference](https://docs.stripe.com/keys/permissions-reference.md). Load the key from the `STRIPE_SECRET_KEY` environment variable, following the [key management guidance](https://docs.stripe.com/keys-best-practices.md).

These examples use `latest_charge`, which is available in API version 2022-11-15 and later. If your integration uses an older API version, [upgrade](https://docs.stripe.com/upgrades.md) before using these examples with a current SDK.

#### Node.js

```javascript
// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

async function exampleFunction(args) {
    try {
      const paymentIntent = await stripe.paymentIntents.create(args);
    } catch (e) {
      if (e.type === 'StripeCardError') {
        let charge;
        const latestCharge = e.payment_intent?.latest_charge;
        const chargeId = typeof latestCharge === 'string' ? latestCharge : latestCharge?.id;
        if (chargeId) {
          try {
            charge = await stripe.charges.retrieve(chargeId);
          } catch (lookupError) {
            console.log('Could not retrieve the charge; using the original payment error.');
          }
        }
        if (charge?.outcome?.type === 'blocked') {
          console.log('Payment blocked for suspected fraud.')
        } else if (e.code === 'card_declined') {
          console.log('Payment declined.')
        } else if (e.code === 'expired_card') {
          console.log('Card expired.')
        } else {
          console.log('Other card error.')
        }
      }
    }
  }
```

### Test payment errors

Use test cards in a sandbox to trigger the error types your integration handles:

- [Blocked payments](https://docs.stripe.com/testing.md#fraud-prevention)
- [Declines and other errors](https://docs.stripe.com/testing.md#declined-payments)

Run the following scenarios with the error handler to check its response.

#### Error to trigger - Blocked for suspected fraud

#### Node.js

```javascript
exampleFunction({
  currency: 'usd',
  amount: 2000,
  confirm: true,
  payment_method_types: ['card'],
  payment_method: 'pm_card_radarBlock', // This is a test card that is always blocked for suspected fraud when you use it in a sandbox. Use it and other test cards to test error handling in your integration. In production code, you would use a real payment method here.
})
```

```
Payment blocked for suspected fraud.
```

#### Error to trigger - Declined by the issuer

#### Node.js

```javascript
exampleFunction({
  currency: 'usd',
  amount: 2000,
  confirm: true,
  payment_method_types: ['card'],
  payment_method: 'pm_card_visa_chargeDeclined', // This is a test card that is always declined by the issuer when you use it in a sandbox. Use it and other test cards to test error handling in your integration. In production code, you would use a real payment method here.
})
```

```
Payment declined.
```

#### Error to trigger - Card expired

#### Node.js

```javascript
exampleFunction({
  currency: 'usd',
  amount: 2000,
  confirm: true,
  payment_method_types: ['card'],
  payment_method: 'pm_card_chargeDeclinedExpiredCard', // This is a test card that always fails as expired when you use it in a sandbox. Use it and other test cards to test error handling in your integration. In production code, you would use a real payment method here.
})
```

```
Card expired.
```

#### Error to trigger - Other card error

#### Node.js

```javascript
exampleFunction({
  currency: 'usd',
  amount: 2000,
  confirm: true,
  payment_method_types: ['card'],
  payment_method: 'pm_card_chargeDeclinedProcessingError', // This is a test card that always fails due to a processing error when you use it in a sandbox. Use it and other test cards to test error handling in your integration. In production code, you would use a real payment method here.
})
```

```
Other card error.
```

### Payment blocked for suspected fraud

|  |
| **Type** | `StripeCardError` |
| **Charge outcome** | Retrieve the charge as shown in the [Payment errors](https://docs.stripe.com/error-handling.md#payment-errors) example, which checks whether a charge is available and handles lookup failures. After a successful lookup, check its outcome:

`charge?.outcome?.type === 'blocked'`

If no charge or outcome is available, don’t infer that the payment was blocked. |
| **Problem** | The Stripe fraud prevention system, *Radar* (Radar Plus helps you fine-tune how Radar operates, get fraud insights on suspicious charges, and assess your fraud management performance from a unified dashboard), blocked the payment |
| **Solutions** | This error can occur when your integration is working correctly. Catch it and prompt the customer for a different payment method.

To block fewer legitimate payments, try these:

- [Optimise your Radar integration](https://docs.stripe.com/radar/optimize-risk-factors.md) to collect more detailed information.
- Use [Payment Links](https://docs.stripe.com/payment-links.md), [Checkout](https://docs.stripe.com/payments/checkout.md), or [Stripe Elements](https://docs.stripe.com/payments/elements.md) for pre-built optimised form elements.

*Radar Plus* (Radar Plus helps you fine-tune how Radar operates, get fraud insights on suspicious charges, and assess your fraud management performance from a unified dashboard) customers have these additional options:

- To exempt a specific payment, add it to your allowlist. (Radar Plus)
- To change your risk tolerance, adjust your [risk settings](https://docs.stripe.com/radar/risk-settings.md). (Radar Plus)
- To change the criteria for blocking a payment, use [custom rules](https://docs.stripe.com/radar/rules.md). (Radar Plus)

You can test your integration’s settings with [test cards that simulate fraud](https://docs.stripe.com/radar/testing.md). If you have custom Radar rules, follow the testing advice in the [Radar documentation](https://docs.stripe.com/radar/testing.md). |

### Payment declined by the issuer

|  |
| **Type** | `StripeCardError` |
| **Codes** | `e.code === 'card_declined'` |
| **Problem** | The card issuer declined the payment. |
| **Solutions** | This error can occur when your integration is working correctly. It reflects an action by the issuer, and that action might be legitimate. Use the decline code to determine what next steps are appropriate. See the [documentation on decline codes](https://docs.stripe.com/declines/codes.md) for appropriate responses to each code.

You can also:

- [Follow recommendations to reduce issuer declines](https://docs.stripe.com/declines/card.md#reducing-bank-declines).
- Use [Payment Links](https://docs.stripe.com/payment-links.md), [Checkout](https://docs.stripe.com/payments/checkout.md), or [Stripe Elements](https://docs.stripe.com/payments/elements.md) for pre-built form elements that implement those recommendations.

Test how your integration handles declines with [test cards that simulate successful and declined payments](https://docs.stripe.com/radar/testing.md). |

### Other payment errors

|  |
| **Type** | `StripeCardError` |
| **Problem** | Another payment error occurred. |
| **Solutions** | This error can occur when your integration is working correctly. Use the error code to determine what next steps are appropriate. See the [documentation on error codes](https://docs.stripe.com/error-codes.md) for appropriate responses to each code. |

## Invalid request errors

|  |
| **Type** | `StripeInvalidRequestError` |
| **Problem** | You made an API call with the wrong parameters, in the wrong state, or in an invalid way. |
| **Solutions** | In most cases, the problem is with the request itself. Either its parameters are invalid or it can’t be carried out in your integration’s current state.
- Consult the [error code documentation](https://docs.stripe.com/error-codes.md) for details on the problem.
- If present, follow the link at `e.doc_url` for documentation about the error code. Check that the error object exists before accessing its properties.
- If the error involves a specific parameter, use `e.param` to determine which one. |

## Connection errors

|  |
| **Type** | `StripeConnectionError` |
| **Problem** | There was a network problem between your server and Stripe. |
| **Solutions** | Treat the result of the API call as indeterminate. That is, don’t assume that it succeeded or that it failed.

To find out if it succeeded, you can:

- Retrieve the relevant object from Stripe and check its status.
- Listen for webhook notification that the operation succeeded or failed. [Verify webhook signatures](https://docs.stripe.com/webhooks.md#verify-events) before processing events. If you restrict inbound traffic by IP address, allow Stripe’s [public webhook IP addresses](https://docs.stripe.com/ips.md).

To help recover from connection errors, you can:

- For API v1, use an [idempotency key](https://docs.stripe.com/api/idempotent_requests.md) when creating or updating an object. Retry a connection error with the same key and parameters within the first 24 hours after the original request. After that window, Stripe can remove the key, and reusing it can execute a new request. If the outcome is still unknown, reconcile it before repeating the operation. See [Advanced error handling](https://docs.stripe.com/error-low-level.md#idempotency) for details, and [API v2 idempotency](https://docs.stripe.com/api-v2-overview.md#idempotency) for API v2 behaviour.
- Configure [automatic retries for your SDK](https://docs.stripe.com/error-low-level.md#network-retries). Stripe SDKs generate idempotency keys and repeat requests when it’s safe to do so. Defaults vary by SDK and version.

This error can mask others. It’s possible that when the connection error resolves, some other error becomes apparent. Check for errors in all of these solutions just as you would in the original request. |

## API errors

|  |
| **Type** | `StripeAPIError` |
| **Problem** | Something went wrong on Stripe’s end. (These are rare.) |
| **Solutions** | Treat the result of the API call as indeterminate. That is, don’t assume that it succeeded or that it failed.

Rely on *webhooks* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) for information about the outcome. Whenever possible, Stripe fires webhooks for any new objects we create as we solve a problem.

To set your integration up for maximum robustness in unusual situations, see [this advanced discussion of server errors.](https://docs.stripe.com/error-low-level.md#server-errors) |

## Authentication errors

|  |
| **Type** | `StripeAuthenticationError` |
| **Problem** | Stripe can’t authenticate you with the information provided. |
| **Solutions** | - Use the correct [API key](https://docs.stripe.com/keys.md).
- Make sure you aren’t using a key that you [“rotated” or revoked](https://docs.stripe.com/keys.md#rolling-keys). |

## Idempotency errors

|  |
| **Type** | `StripeIdempotencyError` |
| **Problem** | You used an [idempotency key](https://docs.stripe.com/api/idempotent_requests.md) for something unexpected, like replaying a request but passing different parameters. |
| **Solutions** | - After you use an idempotency key, only reuse it for identical API calls.
- Use idempotency keys under the limit of 255 characters. |

## Permission errors

|  |
| **Type** | `StripePermissionError` |
| **Problem** | The API key used for this request doesn’t have the necessary permissions. |
| **Solutions** | - Make sure you aren’t using a [restricted API key](https://docs.stripe.com/keys-best-practices.md#limit-access) for a service it doesn’t have access to.
- Don’t perform actions in the Dashboard while logged in as a [user role](https://docs.stripe.com/get-started/account/teams/roles.md) that lacks permission. |

## Rate limit errors

|  |
| **Type** | `StripeRateLimitError` |
| **Problem** | You made too many API calls in too short a time. |
| **Solutions** | - If a single API call triggers this error, wait and try it again.
- To handle rate-limiting automatically, retry the API call after a delay, and increase the delay exponentially if the error continues. See the documentation on [rate limits](https://docs.stripe.com/rate-limits.md) for further advice.
- If you anticipate a large increase in traffic and want to request an increased rate limit, [contact support](https://support.stripe.com/) in advance. |

## Signature verification errors

|  |
| **Type** | `StripeSignatureVerificationError` |
| **Problem** | You’re using *webhook* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) [signature verification](https://docs.stripe.com/webhooks.md#verify-events) and couldn’t verify that a webhook event is authentic. |
| **Solutions** | This error can occur when your integration is working correctly. If you use webhook signature verification and a third party attempts to send you a fake or malicious webhook, then verification fails and this error is the result. Catch it and respond with a `400 Bad Request` status code.

If you receive this error when you shouldn’t – for instance, with webhooks that you know originate with Stripe –then see the documentation on [checking webhook signatures](https://docs.stripe.com/webhooks.md#verify-events) for further advice. In particular, make sure you’re using the correct endpoint secret. This is different from your API key. |
