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

  1. Verify the webhook endpoint only subscribes to customer.subscription.* events
  2. Log event.id and event.type (already done via ctx.error) and locate the event in the Stripe dashboard to inspect its payload
  3. If the event is genuinely malformed, return 200 and skip instead of throwing so Stripe stops retrying a permanently broken event
  4. 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

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.