hcengineering/platform · error · Error
Missing subscription data
Error message
Missing subscription data
What it means
The Stripe webhook handler for customer.subscription.updated casts event.data.object to Stripe.Subscription and requires it to be non-null before processing. If the event payload has no subscription object (malformed/duplicate/foreign event, or API-version shape change), it logs the event and throws 'Missing subscription data', which makes Stripe retry the webhook delivery.
Source
Thrown at services/payment/pod-payment/src/providers/stripe/webhook.ts:116
} catch (err) {
ctx.error('Failed to process Stripe webhook', { err })
res.status(500).json({ error: 'Internal server error' })
}
}
/**
* Handle subscription.created/updated/deleted
*/
async function handleSubscriptionUpdated (
ctx: MeasureContext,
accountsUrl: string,
serviceToken: string,
event: Stripe.Event
): Promise<void> {
const subscription = event.data.object as Stripe.Subscription
if (subscription == null) {
ctx.error('Missing subscription data', { event })
throw new Error('Missing subscription data')
}
const subscriptionData = transformStripeSubscriptionToData(ctx, subscription)
if (subscriptionData === null) {
ctx.warn('Ignoring subscription in irrelevant state', {
subscriptionId: subscription.id,
status: subscription.status
})
return
}
const accountClient = getAccountClient(accountsUrl, serviceToken)
await accountClient.upsertSubscription(subscriptionData)
ctx.info('Subscription upserted', { subscriptionId: subscription.id, status: subscriptionData.status })
}
View on GitHub (pinned to 63e28dc964)
Solutions
- Verify the webhook endpoint only subscribes to customer.subscription.* events
- Log event.id and event.type (already done via ctx.error) and locate the event in the Stripe dashboard to inspect its payload
- If the event is genuinely malformed, return 200 and skip instead of throwing so Stripe stops retrying a permanently broken event
- Pin/align the Stripe SDK API version with the account's webhook API version
Example fix
// before
const subscription = event.data.object as Stripe.Subscription
if (subscription == null) {
ctx.error('Missing subscription data', { event })
throw new Error('Missing subscription data')
}
// after
const subscription = event.data.object as Stripe.Subscription | undefined
if (subscription == null) {
ctx.warn('Ignoring webhook event without subscription data', { eventId: event.id, type: event.type })
return // do not fail; prevents endless Stripe retries on malformed events
} Defensive patterns
Strategy: type-guard
Validate before calling
// before processing any webhook event if (event.type !== 'customer.subscription.updated') return const subscription = event.data.object as Stripe.Subscription | undefined if (subscription == null || typeof subscription.id !== 'string') return // skip, don't throw
Type guard
function hasSubscriptionObject(event: Stripe.Event): event is Stripe.Event & { data: { object: Stripe.Subscription } } {
const obj = (event.data?.object ?? null) as Stripe.Subscription | null
return obj != null && typeof obj.id === 'string' && typeof obj.status === 'string'
} Try / catch
try {
await handleStripeWebhook(ctx, /* deps */, event)
} catch (err) {
if ((err as Error).message === 'Missing subscription data') {
// log and acknowledge (200) so Stripe stops retrying a permanently malformed event
ctx.warn('Skipping malformed subscription.updated event', { eventId: event.id })
return
}
throw err
} Prevention
- Restrict the webhook endpoint registration to customer.subscription.* events
- Return 200 for unprocessable events instead of throwing, to avoid endless Stripe retries
- Pin the Stripe SDK API version and update webhook handlers when upgrading
- Replay-test webhook handling with the Stripe CLI before deploying
When it happens
Trigger: Receiving a customer.subscription.updated webhook whose data.object is null/empty — e.g. a replayed or corrupted event, an event from an unexpected object type, or a test event fired manually from the Stripe dashboard without a subscription payload.
Common situations: Sending a test webhook from the Stripe CLI/dashboard with a hand-crafted payload; webhook endpoint registered for extra event types whose objects are not subscriptions; Stripe API version upgrade changing event payload shape; duplicates/replays with stripped payloads.
Related errors
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/f1fc4512e9524b6c.
Report an issue: GitHub.