hcengineering/platform · error
Invalid body
Error message
Invalid body
What it means
handlePolarWebhook returns HTTP 400 'Invalid body' when the raw request body is not a non-empty Buffer. The route relies on express.raw() middleware to populate req.body as a Buffer, which validateEvent() needs for exact-byte signature verification.
Source
Thrown at services/payment/pod-payment/src/providers/polar/webhook.ts:43
* Uses @polar-sh/sdk for webhook validation
* Webhooks are sent for checkout and subscription lifecycle events
* Documentation: https://polar.sh/docs/integrate/webhooks/delivery
*/
export async function handlePolarWebhook (
ctx: MeasureContext,
accountsUrl: string,
serviceToken: string,
webhookSecret: string,
req: Request,
res: Response
): Promise<void> {
try {
// Body is a Buffer from express.raw() middleware
const rawBody = req.body as Buffer
if (!(rawBody instanceof Buffer) || rawBody.length === 0) {
ctx.error('Invalid webhook body')
res.status(400).json({ error: 'Invalid body' })
return
}
// Validate webhook signature and parse event
const event = validateEvent(rawBody, req.headers as Record<string, string>, webhookSecret)
// Route to appropriate handler based on event type
switch (event.type) {
case 'subscription.created':
case 'subscription.updated':
case 'subscription.active':
case 'subscription.canceled':
case 'subscription.uncanceled':
case 'subscription.revoked':
void handleSubscriptionUpdated(ctx, accountsUrl, serviceToken, event).catch((err) => {
ctx.error('Failed to process Polar webhook event', { event, err })
})
breakView on GitHub (pinned to 63e28dc964)
Solutions
- Ensure express.raw({ type: '*/*' }) (or the provider-specific content type) middleware is registered on this exact route before the handler
- Send the webhook POST with a non-empty raw JSON payload and Content-Type application/json
- Check any reverse proxy / API gateway isn't transforming or dropping the body
- If using a test client, pass the body as a raw Buffer/string, not a parsed object
Example fix
// before
app.post('/webhooks/polar', handlePolarWebhook)
// after
app.post('/webhooks/polar', express.raw({ type: '*/*' }), handlePolarWebhook) Defensive patterns
Strategy: validation
Validate before calling
const rawBody = req.body as Buffer
if (!(rawBody instanceof Buffer) || rawBody.length === 0) {
throw new Error('Request must be sent through express.raw(); body must be a non-empty Buffer')
} Type guard
function isRawBody(body: unknown): body is Buffer {
return body instanceof Buffer && body.length > 0
} Prevention
- Always mount express.raw({ type: '*/*' }) on raw-signature webhook routes
- Never attach express.json() globally to webhook paths
- Integration-test webhooks with raw Buffer payloads
- Check proxies/gateways for body re-encoding
When it happens
Trigger: express.raw() middleware not applied to the webhook route (req.body parsed as JSON object instead of Buffer); a zero-length POST body; a proxy/gateway stripping or re-encoding the body; sending form-encoded or multipart data.
Common situations: Mounting the webhook handler behind a framework that globally parses JSON bodies before the route; testing with curl without -d data; load balancers (e.g. some API gateways) that buffer and re-serialize the request.
Related errors
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/8c641b5167725731.
Report an issue: GitHub.