hcengineering/platform · error
Failed to cancel subscription ${providerSubscriptionId}
Error message
Failed to cancel subscription ${providerSubscriptionId} What it means
cancelSubscription calls the Polar API to cancel, then converts the returned subscription to internal SubscriptionData via transformPolarSubscriptionToData. If the transform returns null (the returned subscription is in a shape/state the transformer does not recognize), the provider cannot honor the cancellation result and throws 'Failed to cancel subscription <id>'. The API call itself may have succeeded; the failure is in interpreting the response.
Source
Thrown at services/payment/pod-payment/src/providers/polar/provider.ts:262
ctx.info('Polar subscription reconciliation completed', {
polarActiveCount: polarActiveSubscriptions.length,
ourActiveCount: ourActiveSubscriptions.length,
upsertedCount: upsertCount,
staleUpdatedCount: staleCount
})
} catch (err) {
ctx.error('Polar subscription reconciliation failed', { err })
throw err
}
}
async cancelSubscription (ctx: MeasureContext, providerSubscriptionId: string): Promise<SubscriptionData> {
const polarSubscription = await this.polar.cancelSubscription(ctx, providerSubscriptionId)
const subscriptionData = transformPolarSubscriptionToData(polarSubscription)
if (subscriptionData == null) {
throw new Error(`Failed to cancel subscription ${providerSubscriptionId}`)
}
return subscriptionData
}
async uncancelSubscription (ctx: MeasureContext, providerSubscriptionId: string): Promise<SubscriptionData> {
const polarSubscription = await this.polar.uncancelSubscription(ctx, providerSubscriptionId)
const subscriptionData = transformPolarSubscriptionToData(polarSubscription)
if (subscriptionData == null) {
throw new Error(`Failed to uncancel subscription ${providerSubscriptionId}`)
}
return subscriptionData
}
async updateSubscriptionPlan (
ctx: MeasureContext,
subscriptionId: string,View on GitHub (pinned to 63e28dc964)
Solutions
- Inspect the raw Polar response for that subscriptionId and update transformPolarSubscriptionToData to handle its shape/status
- Check whether the subscription is already canceled server-side (idempotent retry path) before re-failing
- Pin/align the Polar SDK version with the transformer's expectations
- Add logging in the transformer to record why it returned null
Example fix
// before
const subscriptionData = transformPolarSubscriptionToData(polarSubscription)
if (subscriptionData == null) {
throw new Error(`Failed to cancel subscription ${providerSubscriptionId}`)
}
// after (caller idempotency guard)
const sub = await getSubscription(ctx, providerSubscriptionId)
if (sub?.status === 'canceled') return sub
const subscriptionData = await provider.cancelSubscription(ctx, providerSubscriptionId) Defensive patterns
Strategy: try-catch
Validate before calling
const current = await provider.getSubscription(ctx, providerSubscriptionId)
if (current == null) throw new Error(`Subscription ${providerSubscriptionId} unknown to provider`) Type guard
function isPolarSubscription(x: unknown): x is PolarSubscription {
return typeof x === 'object' && x !== null && 'id' in x && 'status' in x
} Try / catch
try {
await provider.cancelSubscription(ctx, providerSubscriptionId)
} catch (e) {
if (e.message.startsWith('Failed to cancel subscription')) {
// verify actual state in Polar before retry; may already be canceled
} else throw e
} Prevention
- Make cancellation idempotent: check current status first
- Update transformPolarSubscriptionToData whenever the Polar SDK is upgraded
- Log the raw Polar response inside the transformer on null results
- Pin Polar SDK versions and review changelogs
When it happens
Trigger: polar.cancelSubscription returns a subscription object whose fields (prices, product, status) make transformPolarSubscriptionToData return null — e.g. unexpected status value, missing prices array, or a Polar API schema change — for subscription id <id>.
Common situations: Polar API/SDK version bump changing response shape; subscription already fully canceled/expired so the response omits fields; sandbox vs production product data differences; transformer not updated for new Polar subscription states.
Related errors
- Failed to uncancel subscription ${providerSubscriptionId}
- Missing productIds for plan: ${planKey}
- No products configured for plan: ${planKey}
- Missing subscription data
- Storage error ${error.error}
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/012fc3ef99b6aec5.
Report an issue: GitHub.