hcengineering/platform · critical

Failed to cancel subscription at provider

Error message

Failed to cancel subscription at provider

What it means

This 500 is returned when provider.cancelSubscription(...) throws an exception while communicating with the external payment provider. The underlying error is logged with ctx.error and a generic message is sent to the client. The subscription may still be active at the provider, so state should be re-checked after the failure.

Source

Thrown at services/payment/pod-payment/src/server.ts:292

        async (ctx) => {
          const subscriptionId = req.params.subscriptionId

          // Get subscription from our database using internal ID
          const subscription = await accountClient.getSubscriptionById(subscriptionId)

          if (subscription === undefined || subscription === null) {
            res.status(404).json({ error: 'Subscription not found' })
            return
          }

          let canceledSubscription: SubscriptionData | null

          try {
            // Cancel via provider using the provider's subscription ID
            canceledSubscription = await provider.cancelSubscription(ctx, subscription.providerSubscriptionId)
          } catch (err) {
            ctx.error('Failed to cancel subscription at provider', { err })
            res.status(500).json({ error: 'Failed to cancel subscription at provider' })
            return
          }

          if (canceledSubscription === null) {
            res.status(404).json({ error: 'Failed to cancel subscription at provider' })
            return
          }

          // Upsert the updated subscription into our database
          await accountClient.upsertSubscription(canceledSubscription)

          res.status(200).json(canceledSubscription)
        },
        req,
        res,
        () => {}
      )
    }

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Inspect server logs for the ctx.error payload to see the provider's actual error.
  2. Check the subscription's status at the provider dashboard — it may already be canceled; if so, reconcile the local record.
  3. Verify provider credentials and that the configured provider account matches the one that owns providerSubscriptionId.
  4. Retry with backoff if the provider reports a transient (5xx/network) error.
  5. If the provider ID is permanently invalid, delete or flag the local subscription record as unsynced and recreate it.

Example fix

// before
await provider.cancelSubscription(ctx, sub.providerSubscriptionId) // throws: id no longer exists at provider

// after (reconcile first)
const remote = await provider.getSubscription(ctx, sub.providerSubscriptionId)
if (remote === null) await accountClient.upsertSubscription({ ...sub, status: 'canceled' })
else await provider.cancelSubscription(ctx, sub.providerSubscriptionId)
Defensive patterns

Strategy: retry

Validate before calling

// check the subscription state remotely before canceling
const remote = await provider.getSubscription(ctx, sub.providerSubscriptionId)
if (remote === null || remote.status === 'canceled') {
  // reconcile locally instead of calling cancel again
}

Type guard

function isKnownError(err: unknown): err is Error & { code?: string } {
  return err instanceof Error
}

Try / catch

try {
  await cancelSubscription(id)
} catch (e) {
  if (isTransient(e)) { await backoffRetry(() => cancelSubscription(id), 3) }
  else { log.error(e); alertProviderMismatch(id) }
}

Prevention

When it happens

Trigger: POST /api/v1/subscriptions/:subscriptionId/cancel where the local subscription exists but provider.cancelSubscription throws — e.g. providerSubscriptionId unknown to the provider (already deleted), invalid provider credentials, provider API outage, or network timeout.

Common situations: Subscription already canceled/deleted at the provider so its ID is no longer valid; stale providerSubscriptionId after a provider-side data reset; expired provider API keys; provider downtime; sandbox/prod mismatch between stored IDs and configured provider account.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/45b13d0701effccf. Report an issue: GitHub.