hcengineering/platform · error

Missing subscription data

Error message

Missing subscription data

What it means

handleSubscriptionUpdated (invoked from handlePolarWebhook for subscription.updated events) reads the subscription payload as event.data ?? event. If both are null/undefined the webhook carries no subscription object, so after logging it throws 'Missing subscription data' — the event cannot be processed at all. It protects the transformer from being handed an empty payload.

Source

Thrown at services/payment/pod-payment/src/providers/polar/webhook.ts:93

    ctx.error('Failed to process Polar webhook', { err })
    res.status(500).json({ error: 'Internal server error' })
  }
}

/**
 * Handle subscription.created/updated/active
 */
async function handleSubscriptionUpdated (
  ctx: MeasureContext,
  accountsUrl: string,
  serviceToken: string,
  event: any
): Promise<void> {
  const subscription = event.data ?? event
  if (subscription == null) {
    ctx.error('Missing subscription data', { event })
    throw new Error('Missing subscription data')
  }

  const subscriptionData = transformPolarSubscriptionToData(subscription)

  if (subscriptionData === null) {
    ctx.info('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. Inspect the raw webhook body/logged event to confirm the payload shape and re-send a valid subscription.updated event from Polar
  2. Fix the webhook dispatch so only subscription events with data reach handleSubscriptionUpdated
  3. If testing, send a payload with subscription data under event.data (or as event itself)
  4. Return 4xx for such deliveries so Polar's retry/DLQ semantics kick in instead of crashing the handler

Example fix

// before
const event = JSON.parse('{}')
await handlePolarWebhook(ctx, event)
// after
const event = JSON.parse(body)
if (event?.data?.object == null && event?.id == null) {
  return res.status(400).send('missing subscription data')
}
await handlePolarWebhook(ctx, event)
Defensive patterns

Strategy: validation

Validate before calling

if (event?.data == null && event == null) {
  return res.status(400).send('Webhook payload has no subscription data')
}
await handlePolarWebhook(ctx, serviceToken, event)

Type guard

function hasSubscriptionData(event: any): boolean {
  const subscription = event?.data ?? event
  return subscription != null && typeof subscription === 'object' && 'id' in subscription
}

Try / catch

try {
  await handlePolarWebhook(ctx, serviceToken, event)
} catch (e) {
  if (e.message === 'Missing subscription data') {
    // ack with 400 so Polar retries/reports the delivery instead of crashing worker
  } else throw e
}

Prevention

When it happens

Trigger: A Polar webhook delivery whose body lacks event.data and event itself is null — e.g. misrouted/malformed webhook payload, non-subscription event reaching this handler, a manually replayed/test event with an empty body, or signature-verified but truncated payload.

Common situations: Webhook endpoint receiving events from a Polar API version with a different envelope; manual testing with curl sending {} ; replaying events from a queue where the payload was stripped; a Polar outage producing empty event bodies.

Related errors


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