{"record":{"id":"45fcdbded7604175","repo":"hcengineering/platform","slug":"missing-subscription-data","errorCode":null,"errorMessage":"Missing subscription data","messagePattern":"Missing subscription data","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/payment/pod-payment/src/providers/polar/webhook.ts","lineNumber":93,"sourceCode":"\n    ctx.error('Failed to process Polar webhook', { err })\n    res.status(500).json({ error: 'Internal server error' })\n  }\n}\n\n/**\n * Handle subscription.created/updated/active\n */\nasync function handleSubscriptionUpdated (\n  ctx: MeasureContext,\n  accountsUrl: string,\n  serviceToken: string,\n  event: any\n): Promise<void> {\n  const subscription = event.data ?? event\n  if (subscription == null) {\n    ctx.error('Missing subscription data', { event })\n    throw new Error('Missing subscription data')\n  }\n\n  const subscriptionData = transformPolarSubscriptionToData(subscription)\n\n  if (subscriptionData === null) {\n    ctx.info('Ignoring subscription in irrelevant state', {\n      subscriptionId: subscription.id,\n      status: subscription.status\n    })\n    return\n  }\n\n  const accountClient = getAccountClient(accountsUrl, serviceToken)\n  await accountClient.upsertSubscription(subscriptionData)\n\n  ctx.info('Subscription upserted', { subscriptionId: subscription.id, status: subscriptionData.status })\n}\n","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/payment/pod-payment/src/providers/polar/webhook.ts#L75-L111","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the raw webhook body/logged event to confirm the payload shape and re-send a valid subscription.updated event from Polar","Fix the webhook dispatch so only subscription events with data reach handleSubscriptionUpdated","If testing, send a payload with subscription data under event.data (or as event itself)","Return 4xx for such deliveries so Polar's retry/DLQ semantics kick in instead of crashing the handler"],"exampleFix":"// before\nconst event = JSON.parse('{}')\nawait handlePolarWebhook(ctx, event)\n// after\nconst event = JSON.parse(body)\nif (event?.data?.object == null && event?.id == null) {\n  return res.status(400).send('missing subscription data')\n}\nawait handlePolarWebhook(ctx, event)","handlingStrategy":"validation","validationCode":"if (event?.data == null && event == null) {\n  return res.status(400).send('Webhook payload has no subscription data')\n}\nawait handlePolarWebhook(ctx, serviceToken, event)","typeGuard":"function hasSubscriptionData(event: any): boolean {\n  const subscription = event?.data ?? event\n  return subscription != null && typeof subscription === 'object' && 'id' in subscription\n}","tryCatchPattern":"try {\n  await handlePolarWebhook(ctx, serviceToken, event)\n} catch (e) {\n  if (e.message === 'Missing subscription data') {\n    // ack with 400 so Polar retries/reports the delivery instead of crashing worker\n  } else throw e\n}","preventionTips":["Validate and verify webhook signatures before dispatching","Filter events by type before routing to handleSubscriptionUpdated","Log raw bodies (redacted) for all webhook deliveries to debug malformed payloads","Test webhook handling with Polar's example payloads and empty-payload edge cases"],"tags":["payment","polar","webhook","payload"],"backgroundTag":"webhook-payload-missing-data","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}