ARTICLE
Stripe Decline Codes Explained: What Each One Means — And Whether to Retry

A failed charge isn’t one problem. It’s at least eight different problems wearing the same “payment failed” label.
Stripe attaches a decline code to every failed charge — a short string like insufficient_funds or do_not_honor that names why the issuer refused the transaction. Some of those reasons resolve in a day. Others never will, no matter how many times you retry.
Treating every decline the same way is the most common mistake in Stripe payment recovery. This guide covers the decline codes that show up most on subscription billing — what each one means, and whether retrying pays off or wastes an attempt.
Quick Reference: Stripe Decline Codes at a Glance
| Decline code | Plain-English meaning | Retry? |
| insufficient_funds | Not enough money in the account right now | Yes — timed to a likely payday |
| do_not_honor | Bank’s generic refusal, often fraud-risk related | Yes — a few spaced attempts |
| expired_card | Card is past its expiration date | No — request a new card |
| generic_decline | Issuer declined without specifying why | Yes — short delay, then retry |
| card_velocity_exceeded | Too many transactions or too much spend in a short window | Yes — after the velocity window resets |
| lost_card | Cardholder reported the card lost | No — never retry |
| stolen_card | Cardholder reported the card stolen | No — never retry |
| incorrect_cvc | Security code entered was wrong | No — request corrected details |
| processing_error | Something went wrong on the processing side | Yes — retry shortly, then escalate if it repeats |
Soft Declines vs. Hard Declines: The Distinction That Decides Your Strategy
Every decline code falls into one of two buckets. Which bucket it’s in determines whether retrying makes sense at all.
Soft declines happen when the card is still valid but the transaction failed for a temporary reason — low balance, a velocity flag, a brief issuer outage. Retry at the right time and the same card often clears without the customer doing anything.
Hard declines mean the card itself won’t work again. Expired, reported lost or stolen, or the account is closed. Retrying a hard decline doesn’t just fail — issuers can flag repeated attempts on a dead card as suspicious activity, which makes your next legitimate charge on that customer harder to approve too.
Stripe’s own guidance caps retries at eight attempts for charges that allow retrying, and warns that over-retrying can itself trigger more declines. Retry logic isn’t just about getting the payment through. It’s about not burning trust with the issuer in the process.
The 8 Decline Codes Behind Most Failed Subscription Payments
insufficient_funds
What it means: The account doesn’t have enough balance to cover the charge at this exact moment. This is the most common decline code on subscription billing, and the most recoverable.
Retry? Yes. This is a soft decline — the card is fine, the timing is wrong.
What a smart recovery flow does instead of blind retries: A flat “retry every three days” schedule ignores how money actually moves. Most consumers get paid on a schedule — Fridays, the 1st, or the 15th. Timing the retry to land near a likely payday recovers meaningfully more than retrying on a fixed interval that has nothing to do with when the customer’s balance refills.
do_not_honor
What it means: The bank’s catch-all refusal. Stripe doesn’t get a specific reason from the issuer — it can mean a fraud-risk score triggered, a daily spending limit was hit, or an account-level flag exists. There’s no way to know which from the code alone.
Retry? Yes, but carefully. This is treated as a soft decline, though the ambiguity means it deserves a shorter leash than insufficient_funds.
What a smart recovery flow does instead of blind retries: Because the cause is unclear, space retries out — a few attempts, days apart, rather than back-to-back. If it keeps failing after two or three tries, stop retrying and route the customer to an update-card flow instead of continuing to hammer a card the bank has already flagged.
expired_card
What it means: The card’s expiration date has passed. Straightforward — there’s no ambiguity here.
Retry? No. This is a hard decline. The same card number will never clear again.
What a smart recovery flow does instead of blind retries: Skip retries entirely and go straight to an update-card prompt — email, SMS, in-app, whatever channel reaches the customer fastest. Every retry attempt on an expired card is a wasted cycle that delays the one action that actually fixes it.
generic_decline
What it means: The issuer declined the charge without specifying why. Card networks route a large share of declines into this bucket rather than surfacing a precise reason, so the exact cause stays unclear.
Retry? Yes, cautiously. Stripe treats this as a soft decline that permits retrying.
What a smart recovery flow does instead of blind retries: Because the code carries no diagnostic detail, use a short delay — hours, not weeks — before the first retry, then fall back to a customer-facing update flow if it fails again. Don’t treat generic_decline as automatically safe to retry indefinitely just because it’s technically retryable.
card_velocity_exceeded
What it means: The card has been used too many times, or for too much money, within a given window. This usually isn’t about your business specifically — it’s the issuer’s fraud system throttling overall card activity.
Retry? Yes, once the velocity window has likely reset. Retrying immediately after this decline almost never works, because the same limit is still active.
What a smart recovery flow does instead of blind retries: Build in a longer delay than you would for insufficient_funds — a day or more — before the next attempt. Retrying within minutes just adds another attempt to the same velocity count and can push the card further into a fraud hold.
lost_card and stolen_card
What it means: The cardholder has reported the card lost or stolen. Stripe intentionally surfaces this to you as a generic decline reason rather than the specific one, since sharing “your card was reported stolen” with whoever is holding the checkout session is a security risk.
Retry? Never. This is a hard decline in the strictest sense — the card is deactivated on the issuer’s side, permanently.
What a smart recovery flow does instead of blind retries: Route directly to an update-card request. No retry schedule, no delay logic — any retry attempt here is guaranteed to fail and adds nothing but noise to your decline data.
incorrect_cvc
What it means: The security code entered doesn’t match what the issuer has on file. This is typically a data-entry issue, not a card problem.
Retry? No, not as-is. Retrying the exact same charge with the same incorrect CVC will fail every time.
What a smart recovery flow does instead of blind retries: Prompt the customer to re-enter their card details, with the CVC field highlighted. This is one of the few decline codes where the fix is almost always a quick, self-serve correction rather than a new card entirely.
processing_error
What it means: Something went wrong on the processing side rather than with the card itself — a transient issue with the transaction, not a reason the issuer refused it.
Retry? Yes, shortly after the first attempt. If it happens again, treat it as a signal to escalate rather than keep retrying automatically.
What a smart recovery flow does instead of blind retries: One quick retry is reasonable. Repeated processing_error results on the same card are unusual enough that they warrant a manual look or a support flag, not another automated attempt.
Why Decline-Code-Aware Recovery Outperforms Blind Retries
A flat retry schedule treats expired_card the same as insufficient_funds. That means wasted retry attempts on cards that will never clear, and mistimed retries on cards that would have cleared if they’d hit at the right moment.
Reading the decline code first — and branching the response accordingly — is the difference between a recovery flow and a retry loop that happens to occasionally work.
This is the core mechanic behind how Churnsolution recovers failed Stripe payments: retries and dunning messages are routed based on the actual decline reason, not a single generic schedule applied to every failure. Across Churnsolution customers, this approach has generated $3M+ in recovered revenue. In one customer case study, it produced a 67% reactivation rate on previously failed subscriptions — a case-study result, not an average across all customers.
The 3-Bucket Decision Framework
Before building (or debugging) a retry system, sort every decline code into one of three buckets:
- Retry on a schedule tied to the likely cause — insufficient_funds, do_not_honor, generic_decline, card_velocity_exceeded, processing_error.
- Never retry — request new payment details immediately — expired_card, lost_card, stolen_card, incorrect_cvc.
- Escalate if the same code repeats — any code that fails a second or third time after a reasonable retry, regardless of bucket 1 or 2.
That third bucket matters as much as the first two. A code that should be retryable but keeps failing is telling you something a single decline code can’t — usually that it’s time to stop retrying and start messaging the customer directly.
Internal Links
- Related: How to Recover Failed Stripe Payments Without Losing Subscribers — the broader recovery system this post’s decline-code logic plugs into (smart retries, dunning sequences, win-back flows).
What’s Actually Declining in Your Stripe Account?
Every Stripe account has its own mix of decline codes. Some businesses see mostly insufficient_funds. Others see card_velocity_exceeded piling up from international cards. The right retry strategy follows your data, not a generic playbook.
Book a free retention audit and we’ll walk through your decline breakdown — code by code — and show what a decline-code-aware recovery flow would recover on your Stripe account: https://churnsolution.com/demo
No slides. Just your numbers.
- Quick Reference: Stripe Decline Codes at a Glance
- Soft Declines vs. Hard Declines: The Distinction That Decides Your Strategy
- The 8 Decline Codes Behind Most Failed Subscription Payments
- Why Decline-Code-Aware Recovery Outperforms Blind Retries
- The 3-Bucket Decision Framework
- Internal Links
- What’s Actually Declining in Your Stripe Account?
