UpdateSubscription fails with "subscription is in a state that cannot be updated" when swapping card

I’m building a “replace card on file” flow for an active subscription using the .NET SDK (Square C# SDK), and the final step — updating the subscription’s card_id — consistently fails with a BAD_REQUEST error.

What I’m trying to do:
Replace a customer’s card on file, then re-point their existing subscription at the new card so future billing cycles charge the new card instead of the old one.

Flow / code:

  1. Delete the old card from the customer:

    customersApi.DeleteCustomerCard(customerId, oldCardId);

  2. Add the new card to the customer:

var body = new CreateCustomerCardRequest.Builder(model.nonce)
.BillingAddress(bodyBillingAddress)
.CardholderName(model.CardholderName)
.VerificationToken(model.squareVerificationToken)
.Build();

customersApi.CreateCustomerCard(model.SquareCustomerId, body);

Update the subscription’s card (this is the step that fails):

public UpdateSubscriptionResponse UpdateCardInExistingSubsciption(string subscriptionId, string cardId)
{
    var existingSubscription = subscriptionsApi.RetrieveSubscription(subscriptionId).Subscription;
    var bodySubscription = new Square.Models.Subscription.Builder()
                            .Id(subscriptionId)
                            .CardId(cardId)
                            .Version(existingSubscription.Version)
                            .Build();
    var body = new UpdateSubscriptionRequest.Builder()
                .Subscription(bodySubscription)
                .Build();
    return subscriptionsApi.UpdateSubscription(subscriptionId, body);
}

After this Error received.
Error Message: “Response Not OK”

UpdateSubscription is a sparse update and only changes the fields you send and leaves everything else at its current server value. So I think your request body should contain just card_id and version, nothing else. Drop Id from the builder; it’s a read-only field and the subscription ID already goes in the path parameter.

The error itself: “state that cannot be updated” is about the subscription’s status, so retrieve it and check. Valid values are PENDING, ACTIVE, PAUSED, CANCELED, DEACTIVATED, and COMPLETED.

  • If it’s DEACTIVATED, you can recover it with ResumeSubscription. Worth knowing: deactivation isn’t caused by card declines — it’s things like a missing or invalid customer email, a deleted customer profile, or a deactivated location. Call ListSubscriptionEvents and look at the DEACTIVATE_SUBSCRIPTION event’s info object for the exact reason code. Fix that first, since the resume call fails unless the underlying issue is resolved. Also note that resuming mid-cycle triggers a prorated invoice.
  • If it’s CANCELED, there’s no documented way to resume it — you’d create a new subscription.
  • If it’s ACTIVE but has a canceled_date, that’s just a scheduled future cancellation, not a terminal state. You can clear it by setting that field to null.

Two other things. “Response Not OK” is hiding the real message — catch ApiException and log the Errors collection to get the category, code, detail, and field. That will tell you exactly what Square is rejecting.

And CreateCustomerCard/DeleteCustomerCard are deprecated in favour of the Cards API (CreateCard/DisableCard); the card ID it returns works fine as card_id. Either way, add the new card and update the subscription before removing the old one, so the subscription is never pointing at a card that no longer exists.

Docs: UpdateSubscription · ResumeSubscription · Subscription billing and deactivation