Error Handling
Understanding and handling Stripe API errors effectively
Stripe API requests can fail for various reasons: invalid parameters, authentication issues, card declines, rate limits, and network failures. The StripeError enum provides structured error information to help you handle these cases appropriately.
Error Types
The StripeError enum has several variants:
/// An error encountered when communicating with the Stripe API.
#[derive(Debug, Error)]
pub enum StripeError {
/// Stripe returned a client error.
#[error("error reported by stripe: {0:#?}, status code: {1}")]
Stripe(Box<ApiErrors>, u16),
/// An error occurred when parsing the Stripe response.
#[error("error deserializing a request: {0}")]
JSONDeserialize(String),
/// An error occurred communicating with Stripe.
#[error("error communicating with stripe: {0}")]
ClientError(String),
/// The client configuration was invalid.
#[error("configuration error: {0}")]
ConfigError(String),
/// A blocking request timed out
#[error("timeout communicating with stripe")]
Timeout,
}Handling API Errors
The most common error type is StripeError::Stripe, which contains the error details from Stripe's API along with the HTTP status code.
Basic Error Handling
match Customer::create(&client, CreateCustomer::new()).send().await {
Ok(customer) => info!("Created customer: {}", customer.id),
Err(err) => match err {
StripeError::Stripe(api_error, status_code) => {
error!("Stripe API error ({}): {:?}", status_code, api_error.message);
}
StripeError::ClientError(msg) => {
error!("Network error: {}", msg);
}
_ => {
error!("Other error: {}", err);
}
},
}Handling Specific HTTP Status Codes
Different status codes indicate different types of failures:
match PaymentIntent::retrieve(&client, &payment_intent_id, &[]).await {
Ok(payment) => {
info!("Payment status: {:?}", payment.status);
}
Err(StripeError::Stripe(api_error, status)) => match status {
400 => {
// Bad Request - Invalid parameters
error!("Invalid request: {:?}", api_error.message);
}
401 => {
// Unauthorized - Invalid API key
error!("Authentication failed - check your API key");
}
402 => {
// Payment Required - Card declined or insufficient funds
error!("Payment failed: {:?}", api_error.message);
// The error may contain a decline code
if let Some(code) = &api_error.code {
match code.as_str() {
"card_declined" => error!("Card was declined"),
"insufficient_funds" => error!("Insufficient funds"),
"expired_card" => error!("Card has expired"),
_ => error!("Payment error code: {}", code),
}
}
}
404 => {
// Not Found - Resource doesn't exist
error!("Resource not found");
}
429 => {
// Too Many Requests - Rate limited
warn!("Rate limited - slow down requests");
}
500 | 502 | 503 | 504 => {
// Server errors - retry with backoff
error!("Stripe server error - retry later");
}
_ => {
error!("HTTP {}: {:?}", status, api_error.message);
}
},
Err(e) => {
error!("Non-API error: {}", e);
}
}
}Common Error Codes
Stripe includes error codes in the ApiErrors struct that provide more specific information about what went wrong:
Payment Errors (402)
card_declined- The card was declinedexpired_card- The card has expiredincorrect_cvc- The CVC is incorrectprocessing_error- An error occurred while processing the cardinsufficient_funds- Insufficient funds in the account
Request Errors (400)
parameter_invalid_empty- A required parameter was emptyparameter_unknown- An unknown parameter was providedresource_missing- The requested resource doesn't exist
Authentication Errors (401)
invalid_api_key- The API key is invalid
For a complete list of error codes, see the Stripe Error Codes documentation.
Retry Strategies
For transient errors (network issues, server errors), use the built-in retry strategies:
let client = Client::builder(secret_key)
.request_strategy(RequestStrategy::ExponentialBackoff(3))
.build();
// Replace with an actual customer ID for testing
let customer_id = "cus_example";
// This request will automatically retry up to 3 times with backoff
let customer = Customer::retrieve(&client, &customer_id, &[]).await;
match customer {
Ok(customer) => info!("Retrieved customer: {}", customer.id),
Err(e) => error!("Failed to retrieve customer after retries: {}", e),
}See the Request Strategies documentation for more details on retry behavior.
Best Practices
1. Handle Specific Errors
Don't just log all errors the same way. Handle payment failures differently from configuration errors:
match result {
Err(StripeError::Stripe(api_error, 402)) => {
// Show user-friendly message for payment failures
show_payment_error_to_user(&api_error);
}
Err(StripeError::Stripe(api_error, 400)) => {
// Log parameter errors for debugging
error!("Invalid parameters: {:?}", api_error);
}
Err(e) => {
// Log unexpected errors and alert monitoring
error!("Unexpected Stripe error: {}", e);
}
Ok(_result) => {
// Success
info!("Operation succeeded");
}
}2. Use Idempotency Keys
For critical operations (especially payment creation), always use idempotency keys to prevent accidental duplicate charges:
let key = IdempotencyKey::new("order_12345").unwrap();
let payment = CreatePaymentIntent::new(1000, Currency::USD)
.request_strategy(RequestStrategy::Idempotent(key))
.send(client)
.await;
match payment {
Ok(payment_intent) => {
info!("Created payment intent: {}", payment_intent.id);
}
Err(e) => {
error!("Failed to create payment: {}", e);
}
}3. Log Error Details
The ApiErrors struct contains useful debugging information:
let result = PaymentIntent::retrieve(client, "pi_example", &[]).await;
if let Err(StripeError::Stripe(api_error, status)) = result {
error!(
status = status,
error_type = ?api_error.error_type,
code = ?api_error.code,
message = ?api_error.message,
param = ?api_error.param,
"Stripe error"
);
}4. Don't Retry Client Errors
4xx errors (except 429 rate limits) usually indicate a problem with your request that won't be fixed by retrying. Only retry 5xx server errors and network failures.
The built-in RequestStrategy::ExponentialBackoff handles this correctly for you.
Never retry failed payments without user confirmation. A failed payment could be intentional (e.g., user canceled) or indicate fraud prevention.