{"record":{"id":"45b13d0701effccf","repo":"hcengineering/platform","slug":"failed-to-cancel-subscription-at-provider","errorCode":null,"errorMessage":"Failed to cancel subscription at provider","messagePattern":"Failed to cancel subscription at provider","errorType":"http","errorClass":null,"httpStatus":500,"severity":"critical","filePath":"services/payment/pod-payment/src/server.ts","lineNumber":292,"sourceCode":"        async (ctx) => {\n          const subscriptionId = req.params.subscriptionId\n\n          // Get subscription from our database using internal ID\n          const subscription = await accountClient.getSubscriptionById(subscriptionId)\n\n          if (subscription === undefined || subscription === null) {\n            res.status(404).json({ error: 'Subscription not found' })\n            return\n          }\n\n          let canceledSubscription: SubscriptionData | null\n\n          try {\n            // Cancel via provider using the provider's subscription ID\n            canceledSubscription = await provider.cancelSubscription(ctx, subscription.providerSubscriptionId)\n          } catch (err) {\n            ctx.error('Failed to cancel subscription at provider', { err })\n            res.status(500).json({ error: 'Failed to cancel subscription at provider' })\n            return\n          }\n\n          if (canceledSubscription === null) {\n            res.status(404).json({ error: 'Failed to cancel subscription at provider' })\n            return\n          }\n\n          // Upsert the updated subscription into our database\n          await accountClient.upsertSubscription(canceledSubscription)\n\n          res.status(200).json(canceledSubscription)\n        },\n        req,\n        res,\n        () => {}\n      )\n    }","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/payment/pod-payment/src/server.ts#L274-L310","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect server logs for the ctx.error payload to see the provider's actual error.","Check the subscription's status at the provider dashboard — it may already be canceled; if so, reconcile the local record.","Verify provider credentials and that the configured provider account matches the one that owns providerSubscriptionId.","Retry with backoff if the provider reports a transient (5xx/network) error.","If the provider ID is permanently invalid, delete or flag the local subscription record as unsynced and recreate it."],"exampleFix":"// before\nawait provider.cancelSubscription(ctx, sub.providerSubscriptionId) // throws: id no longer exists at provider\n\n// after (reconcile first)\nconst remote = await provider.getSubscription(ctx, sub.providerSubscriptionId)\nif (remote === null) await accountClient.upsertSubscription({ ...sub, status: 'canceled' })\nelse await provider.cancelSubscription(ctx, sub.providerSubscriptionId)","handlingStrategy":"retry","validationCode":"// check the subscription state remotely before canceling\nconst remote = await provider.getSubscription(ctx, sub.providerSubscriptionId)\nif (remote === null || remote.status === 'canceled') {\n  // reconcile locally instead of calling cancel again\n}","typeGuard":"function isKnownError(err: unknown): err is Error & { code?: string } {\n  return err instanceof Error\n}","tryCatchPattern":"try {\n  await cancelSubscription(id)\n} catch (e) {\n  if (isTransient(e)) { await backoffRetry(() => cancelSubscription(id), 3) }\n  else { log.error(e); alertProviderMismatch(id) }\n}","preventionTips":["Reconcile local subscription state with the provider on a schedule.","Use exponential backoff with idempotency keys for provider mutations.","Alert on provider auth errors to catch key rotation issues quickly.","Verify environment consistency (sandbox keys never used against prod data)."],"tags":["http-500","payment-provider","external-api","network"],"backgroundTag":"payment-provider-request-failed","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}