Error Handling

    Learn how to handle and recover from errors received from the Stripe API.

    Your Stripe integration might have to deal with errors at some point when making API requests to Stripe. These errors fall into a few major categories:

    Content errors occur because the content in the API request was invalid in some way. They return an HTTP response with a 4xx error code. For example, the API servers might return a 401 if an invalid API key was provided, or a 400 if a required parameter was missing.

    • Network errors occur as a result of intermittent communication problems between client and server. They return low-level errors, like socket or timeout exceptions. For example, a client might time out while trying to read from Stripe’s servers, or an API response might never be received because a connection terminates prematurely. Note that a network error wouldn’t necessarily have otherwise been a successful request — it can also be another type of error that’s been cloaked by an intermittent problem.

    • Server errors occur because of a problem on Stripe’s servers. Server errors return an HTTP response with a 5xx error code. Stripe works to make these errors as rare as possible, but integrations should be able to handle them when they do arise.

    The right approach and idempotency semantics to use for handling errors depend on the type of error being handled.

    Safely retrying requests with idempotency

    A key part of web API design is the idea of idempotency, defined as being able to apply the same operation multiple times without changing the result beyond the first try. Because a certain amount of intermittent failure is to be expected, clients need a way of reconciling failed requests with a server, and idempotency provides a mechanism for that.

    The Stripe API guarantees the idempotency of GET and DELETE requests, so it’s always safe to retry them. Including an idempotency key makes POST requests idempotent, which prompts the API to do the bookkeeping required to prevent duplicate operations. Requests that include an idempotency key can be safely retried as long as the second request occurs within 24 hours from when the key was first received (keys are expired out of the system after 24 hours).

    For example, if a request to create a charge does not respond due to a network connection error, you can retry the request with the same idempotency key to guarantee that no more than one charge is created.

    Sending idempotency keys

    Idempotency keys are sent in the Idempotency-Key header, and you should use them for all POST requests to Stripe’s API. Most official client libraries can send them automatically as long as they’re configured to send retries. See network errors below.

    If you decide to send them manually, make sure that the tokens being used are sufficiently unique to unambiguously identify a single operation within your account over at least the last 24 hours. A few common strategies for generating idempotency keys are:

    • Use an algorithm that generates a token with good randomness, like UUID v4.

    • Derive the key from a user-attached object like the ID of a shopping cart. This provides a relatively easy way to protect against double submissions.

    A response that’s being replayed from the server because it had already executed previously can be identified by the header Idempotent-Replayed: true.

    Content errors

    Content errors are the result of the contents of an API request being invalid and return a 4xx error code. Integrations should correct the original request, and try again. Depending on the type of user error (e.g., a card being declined), it may be possible to handle the problem programmatically. In these cases, include a code field to help an integration react appropriately. See error codes for more details.

    For a POST operation using an idempotency key, as long as an API method began execution, Stripe’s API servers will cache the results of the request regardless of what they were. A request that returns a 400 will send back the same 400 if followed by a new request with the same idempotency key. A fresh idempotency key should be generated when modifying the original request to get a successful result. This operation does contain some caveats. For example, a request that’s rate limited with a 429 can produce a different result with the same idempotency key because rate limiters run before the API’s idempotency layer. The same goes for a 401 that omitted an API key, or most 400s that sent invalid parameters. Even so, the safest strategy where 4xx errors are concerned is to always generate a new idempotency key.

    Network errors

    Network errors are the result of connectivity problems between client and server and tend to manifest as low-level errors like socket or timeout exceptions.

    This class of errors is where the value of idempotency keys and request retries is most obvious. When intermittent problems occur, clients are usually left in a state where they don’t know whether or not the server received the request. To get a definitive answer, they should retry such requests with the same idempotency keys and the same parameters until they’re able to receive a result from the server. Sending the same idempotency with different parameters will produce an error indicating that the new request didn’t match the original.

    Most client libraries can generate idempotency keys and retry requests automatically, but need to be configured to do so. They perform their first retry quickly after the first failure, and subsequent retries on an exponential backoff schedule, the assumption being that a single failure is often a random occurrence, but a pattern of repeated failures likely represents a chronic problem.

    Stripe.max_retries = 2
    
    stripe.max_network_retries = 2
    
    \Stripe\Stripe::setMaxNetworkRetries(2);
    
    stripe.setMaxNetworkRetries(2);
    
    config := &stripe.BackendConfig{
        MaxNetworkRetries: 2,
    }
    
    sc := &client.API{}
    sc.Init("sk_test_...", &stripe.Backends{
        API:     stripe.GetBackendWithConfig(stripe.APIBackend, config),
        Uploads: stripe.GetBackendWithConfig(stripe.UploadsBackend, config),
    })
    
    StripeConfiguration.MaxNetworkRetries = 2;
    

    Server errors

    Server errors are the result of a server-side problem and return a 5xx error code. These errors are the most difficult to handle, so we try to ensure that they happen as infrequently as possible.

    As with user errors, the idempotency layer caches the result of POST mutations that result in server errors (specifically 500s, which are internal server errors), so retrying them with the same idempotency key will usually produce the same result. The request can be retried with a new idempotency key, but we’d advise against it because it’s possible for the original one to have produced side effects.

    The result of a 500 request should be treated as indeterminate. The most likely time to observe one is during a production incident, and generally during such an incident’s remediation. Stripe engineers will examine failed requests and try to appropriately reconcile the results of any mutations that resulted in 500s.

    While the idempotency-cached response to those requests won’t change, we will try to fire webhooks for any new objects created as part of Stripe’s reconciliation. The exact nature of any retroactive changes in the system depend heavily on the type of request. For example, if creating a charge returns a 500 error but we detect that the information has gone out a payment network, we’ll try to roll it forward. If not, we’ll try to roll it back. However, ideal results under these circumstances are not guaranteed, and requests resulting in a 500 error may produce user-visible side effects.

    Integrations that want to maximize robustness must configure webhook handlers that are capable of receiving event objects that have never been seen in an API response. One technique for cross-referencing these new objects with the data from an integration’s local state is to send a local identifier in with the metadata when creating new resources with the API. That identifier will appear in the metadata field of an object going out through a webhook, even if the webhook is generated later as part of reconciliation.

    The Stripe-Should-Retry header

    A client library can’t always determine with certainty if it should retry based solely on a status code or content in the response body. To assist clients in making decisions in cases where the API has additional information about whether a request is retryable, it will tell them explicitly by responding with the header Stripe-Should-Retry.

    • Stripe-Should-Retry set to true indicates that a client should retry the request. Clients should still wait some amount of time (probably determined according to an exponential backoff schedule) before making the next request so as to not overload the API.
    • Stripe-Should-Retry set to false means that a client should not retry the request as it would have no additional effect.
    • Stripe-Should-Retry not set in the response indicates that the API has no specific opinion on whether the request is retryable. Clients should fall back to other properties of the response (like the status code) to make a decision.

    The retry mechanisms built into Stripe’s client libraries respect Stripe-Should-Retry automatically. If you’re using one of them, there’s no need to handle it manually.

    Was this page helpful?

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.

    On this page