The payment failed, the invoice is open, and Stripe never retried
An invoice has been sitting open for five weeks. The first payment attempt failed and the timeline after it is empty. No retry and no email. The customer says their card has worked fine the whole time.
Nothing is broken in a way that produced an error, because what went wrong is an absence.
Why there is no next attempt
next_payment_attempt on the invoice holds the time payment will next be
attempted. Null means Stripe has nothing scheduled. Getting to null happens
several different ways, and there is one way to get a date that never fires.
The retry schedule ran to its end. Stripe retries on either a Smart Retries policy or a custom schedule of up to three retries, and after the final attempt it makes no further attempts. What happens then is an account setting with three options. Cancel the subscription, mark it unpaid, or leave it past due. The third leaves the invoice open and uncollected for as long as nobody looks.
There is no payment method to retry. Stripe does not retry at all when none is available, and the schedule simply has nothing to run against.
The schedule exists and nothing executes it. This is the case that looks least
like a failure. On a hard decline from the issuer, lost_card, stolen_card
and seven other codes, Stripe cannot retry without a new payment
method, and its documented behaviour is that the retries stay scheduled and
attempt_count keeps incrementing while the retries only execute once a new
payment method appears. Unexecuted retries create no Charge. So the counter
climbs, next_payment_attempt holds a date, and no money moves. An invoice in
this state is indistinguishable from a healthy one on both fields you would
think to check.
The invoice is not on automatic collection. With collection_method set to
send_invoice, Stripe emails the invoice and waits for the customer to pay it.
next_payment_attempt is null on those invoices by definition. There is no
saved payment method being retried because retrying was never the plan.
The invoice was never handed to Stripe to drive. auto_advance controls whether
Stripe performs automatic collection, and with it false the invoice’s state does
not advance without an explicit action from you. An invoice created through the
API that way stays a draft.
Collection is paused. pause_collection keeps the subscription active and keeps
invoices generating while Stripe collects nothing. Under the keep_as_draft
behavior those invoices are created as drafts with auto_advance false, waiting
for a person. It outlives the reason it was set, especially when it was set by
hand during a support conversation.
The part that defeats event-driven code
The first failure emitted invoice.payment_failed. That is the last thing your
webhook handler hears. A retry that never gets scheduled emits nothing, and if
the account is set to leave the subscription past due, no subscription event
follows either. An integration built entirely around incoming events is waiting
for a message that will not arrive. The only evidence lives in a field on an
object nobody has a reason to re-read.
Billing automations move that field too. With automations turned on,
next_payment_attempt is no longer set on invoice.payment_failed and is set
on invoice.updated instead, so a handler reading it off the failure event
reads null.
Confirming it
List invoices with status open and read collection_method, auto_advance,
attempted and next_payment_attempt on each one. An invoice on automatic
collection, older than your full retry window, with nothing scheduled, has
stopped. It will stay stopped.
Then open Billing, Revenue recovery, Retries in the Dashboard and read what your account does after the final attempt. It was chosen once during setup, and “leave the subscription past due” is the option that leaves invoices open with nothing chasing them.
Fixing it
Decide the terminal behavior on purpose. Cancelling the subscription or marking the invoice uncollectible both leave a legible record of what happened. Leaving invoices open forever does not, and it is the setting that makes this failure invisible.
For invoices already stranded, get the customer onto a working payment method
and pay the invoice directly, or void it and reissue if the amount is no longer
right. Voiding works on an invoice that is open or uncollectible, and it is
terminal. Set auto_advance true on invoices you create through the API and
expect Stripe to collect.
An unretried failure is revenue you invoiced correctly and then never chased.