Skip to content
Create account or Sign in
The Stripe Docs logo
Search/Ask AI
Create accountSign in
  • Get started
  • Payments
  • Revenue
  • Risk
  • Data
  • Money management
  • Stablecoins
  • Embedded finance
  • Developer resources
More
APIs & SDKsHelp
Overview
Versioning
Changelog
Upgrade your integrationHow API versioning works
Essentials
SDKs
API
    API v2
    Rate limits
    Authentication
    API keys
    Service principals
    Workload Identity Federation
    Specify request context
    Domains and IP addresses
    Make requests
    Expand responses
    Pagination
    Search objects
    Localize content
    Testing and data
    Metadata
    Test your application
    Error handling
    Handle errors
      Advanced error handling
    Error codes
Events
Testing
Stripe CLI
Tools
Stripe Dashboard
Stripe Projects
Provisioning
Workbench
Developers Dashboard
Stripe for Visual Studio Code
Terraform
Stripe Discord server
Features
Workflows
Batch jobs
Stripe health alertsFile uploads
AI tools
Agent pluginsModel Context ProtocolAgent skillsStripe Directory
Extend Stripe
Overview
Build Stripe apps
Use apps from Stripe
Build extensions
Custom objects
Security and privacy
Security
Activity logsStripebot web crawler
Privacy
Partners
Partner ecosystem
Partner certification
United States
English (United States)
  1. Home/
  2. Developer resources/
  3. API

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.

SituationNext action
Parameters are invalid or authentication failsCorrect the request or API key before trying again.
A payment is declined or needs customer actionHandle the payment error and ask the customer to complete the required action.
A connection fails or a request is still executingRetry safely with the same idempotency key and parameters.
A server error or another failure leaves the outcome unknownReconcile the request before starting another operation.

Before you begin

Before running the examples:

  • Install a supported Stripe SDK 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.
  • Create a restricted API key with only the permissions needed by the examples you run. See the permissions reference:
    • 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. For examples that read STRIPE_SECRET_KEY, set that variable to your restricted key.
  • For webhook examples, set up a webhook endpoint, 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
codeThe error code.
doc_urlA link to the Stripe documentation for the specific error code.
messageA description of the problem.
paramThe request parameter associated with the error, if applicable.
request_log_urlA link to the request log in the Stripe Dashboard.
Request IDA 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.
typeThe error category.

Use these techniques to handle errors identified through different channels. Use the error type to choose a response.

TechniqueWhere to use it
Handle API request errorsExceptions or error values returned by individual API requests.
Monitor webhooksFailures reported through events, including failures that occur after an API request completes.
Inspect stored errorsErrors 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.

HTTP errors

For HTTP status codes, idempotency, and retries, see Advanced error handling and the API error reference.

Use Ruby’s rescue keyword to catch Stripe::StripeError or its subclasses. Each subclass represents a different kind of error. Use the exception class to choose a response.

Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. def example_function(params) client = Stripe::StripeClient.new(ENV.fetch('STRIPE_SECRET_KEY')) begin client.v1.payment_intents.create(params) rescue Stripe::CardError => e puts "A payment error occurred: #{e.error.message} (request id: #{e.request_id})" rescue Stripe::InvalidRequestError => e puts "An invalid request occurred. (request id: #{e.request_id})" # Add additional catch cases for other Stripe exception types as needed. # See all exception types at https://docs.stripe.com/api/errors/handling?lang=ruby rescue Stripe::StripeError => e # All other Stripe errors puts "Status: #{e.http_status}, Code: #{e.code}, Message: #{e.message}, Request ID: #{e.request_id}" else puts "No error." end end

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.

Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
example_function( # The required parameter currency is missing, amount: 2000, confirm: true, payment_method_types: ['card'], payment_method: 'pm_card_visa', )
console
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
An invalid request occurred.

Monitor webhooks

Use webhooks 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 a payment, then disconnects before receiving the result, but your server can still receive the failure event.

In your webhook handler, verify the signature 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.

The following examples handle snapshot events, which include the affected object in data.object. For thin events, follow the thin-event processing guidance to retrieve the details you need.

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

  1. Access event.data.object to retrieve the affected object.
  2. Inspect the stored error, if the object contains one.
  3. Use the error type to choose a response.
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
require 'stripe' require 'sinatra' endpoint_secret = ENV.fetch('STRIPE_WEBHOOK_SECRET') post '/webhook' do payload = request.body.read begin event = Stripe::Webhook.construct_event( payload, request.env['HTTP_STRIPE_SIGNATURE'], endpoint_secret ) rescue JSON::ParserError, Stripe::SignatureVerificationError halt 400 end # Use the event type to find out what happened case event.type when 'payment_intent.payment_failed' # Get the object affected payment_intent = event.data.object # Use stored information to get an error object e = payment_intent.last_payment_error halt 200 if e.nil? # Use its type to choose a response case e.type when 'card_error' puts "A payment error occurred: #{e.message}" when 'invalid_request_error' puts "An invalid request occurred." else puts "Another problem occurred, maybe unrelated to Stripe." end end content_type 'application/json' { status: 'success' }.to_json end

Test webhook handling

Set up local webhook testing, then trigger a failed payment to test your handler:

Command Line
stripe trigger payment_intent.payment_failed
Output
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.

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.
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = Stripe::StripeClient.new('sk_test_...') payment_intent = client.v1.payment_intents.retrieve('{{PAYMENT_INTENT_ID}}') e = payment_intent.last_payment_error if !e.nil? puts "PaymentIntent #{payment_intent.id} experienced a #{e.type}." end

Here are common objects that store information about failures.

ObjectAttributeValues
PaymentIntentlast_payment_errorAn error object
SetupIntentlast_setup_errorAn error object
Invoicelast_finalization_errorAn error object
SetupAttemptsetup_errorAn error object
Payoutfailure_codeA payout failure code
Refundfailure_reasonA refund failure code

Test stored errors

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

  • Declined payment.
  • Failed payout.
  • Failed refund.

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 Ruby library, exceptions belong to Stripe::StripeError and its subclasses. Use the documentation for each class for advice on responding.

Name

SDK exception

Description
Payment error

Stripe::CardError

An error occurred during a payment, involving one of these situations:
  • Blocked payments
  • Issuer declines.
  • Other payment errors.
Invalid request error

Stripe::InvalidRequestError

You made an API call with the wrong parameters, in the wrong state, or in an invalid way.

Connection error

Stripe::APIConnectionError

There was a network problem between your server and Stripe.
API error

Stripe::APIError

Something went wrong on Stripe’s end. (These are rare.)
Authentication error

Stripe::AuthenticationError

Stripe can’t authenticate you with the information provided.
Idempotency error

Stripe::IdempotencyError

You used an idempotency key for something unexpected, like replaying a request but passing different parameters.
Permission error

Stripe::PermissionError

The API key used for this request doesn’t have the necessary permissions.
Rate limit error

Stripe::RateLimitError

You made too many API calls in too short a time.
Signature verification error

Stripe::SignatureVerificationError

You’re using webhook signature verification 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
  • Issuer declines
  • Other payment errors

To choose a response, check the error code, decline code, and charge outcome. To identify the correct charge, check whether the error contains a PaymentIntent with a 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 with payment_intent_write to create PaymentIntent objects and charge_read to retrieve Charge objects. See the permissions reference. Load the key from the STRIPE_SECRET_KEY environment variable, following the key management guidance.

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 before using these examples with a current SDK.

Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
require 'stripe' def example_function(params) client = Stripe::StripeClient.new(ENV.fetch('STRIPE_SECRET_KEY')) begin client.v1.payment_intents.create(params) rescue Stripe::CardError => e charge = nil charge_id = e.error&.payment_intent&.latest_charge if charge_id begin charge = client.v1.charges.retrieve(charge_id) rescue Stripe::StripeError warn 'Could not retrieve the charge; using the original payment error.' end end if charge&.outcome&.type == 'blocked' puts 'Payment blocked for suspected fraud.' elsif e.code == 'card_declined' puts 'Payment declined.' elsif e.code == 'expired_card' puts 'Card expired.' else puts 'Other card error.' end end end

Test payment errors

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

  • Blocked payments
  • Declines and other errors

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

Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
example_function( currency: 'usd', amount: 2000, confirm: true, payment_method_types: ['card'], payment_method: 'pm_card_radarBlock', )
console
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
Payment blocked for suspected fraud.

Payment blocked for suspected fraud

Type

Stripe::CardError

Charge outcome

Retrieve the charge as shown in the 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.

ProblemThe Stripe fraud prevention system, Radar, 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:

  • Optimize your Radar integration to collect more detailed information.
  • Use Payment Links, Checkout, or Stripe Elements for prebuilt optimized form elements.

Radar Plus 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. Radar Plus
  • To change the criteria for blocking a payment, use custom rules. Radar Plus

You can test your integration’s settings with test cards that simulate fraud. If you have custom Radar rules, follow the testing advice in the Radar documentation.

Payment declined by the issuer

Type

Stripe::CardError

Codes

e.error.code == "card_declined"

ProblemThe 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 for appropriate responses to each code.

You can also:

  • Follow recommendations to reduce issuer declines.
  • Use Payment Links, Checkout, or Stripe Elements for prebuilt form elements that implement those recommendations.

Test how your integration handles declines with test cards that simulate successful and declined payments.

Other payment errors

Type

Stripe::CardError

ProblemAnother payment error occurred.
SolutionsThis 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 for appropriate responses to each code.

Invalid request errors

Type

Stripe::InvalidRequestError

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 for details on the problem.
  • If present, follow the link at e.error&.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

Stripe::APIConnectionError

ProblemThere 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 before processing events. If you restrict inbound traffic by IP address, allow Stripe’s public webhook IP addresses.

To help recover from connection errors, you can:

  • For API v1, use an idempotency key 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 for details, and API v2 idempotency for API v2 behavior.
  • Configure automatic retries for your SDK. 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

Stripe::APIError

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

Authentication errors

Type

Stripe::AuthenticationError

ProblemStripe can’t authenticate you with the information provided.
Solutions
  • Use the correct API key.
  • Make sure you aren’t using a key that you “rotated” or revoked.

Idempotency errors

Type

Stripe::IdempotencyError

ProblemYou used an idempotency key 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

Stripe::PermissionError

ProblemThe API key used for this request doesn’t have the necessary permissions.
Solutions
  • Make sure you aren’t using a restricted API key for a service it doesn’t have access to.
  • Don’t perform actions in the Dashboard while logged in as a user role that lacks permission.

Rate limit errors

Type

Stripe::RateLimitError

ProblemYou 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 for further advice.
  • If you anticipate a large increase in traffic and want to request an increased rate limit, contact support in advance.

Signature verification errors

Type

Stripe::SignatureVerificationError

ProblemYou’re using webhook signature verification 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 for further advice. In particular, make sure you’re using the correct endpoint secret. This is different from your API key.

Was this page helpful?
YesNo
  • Need help? Contact Support.
  • Chat with Stripe developers on Discord.
  • Check out our changelog.
  • Questions? Contact Sales.
  • LLM? Read llms.txt.
  • Powered by Markdoc
On this page
Advertisement
Advertisement