yikart/AiToEarn · warning

ChannelWebhookInvalidVerifyToken

Error message

ChannelWebhookInvalidVerifyToken

What it means

Facebook webhook subscription verification (handleChallenge, called from handle for GET hub.mode=subscribe requests) responds 403 with ChannelWebhookInvalidVerifyToken when the hub.verify_token query parameter does not equal config.webhookVerifyToken or the challenge is missing. Meta sends this token once when you register the webhook URL; mismatch means Meta cannot complete subscription verification.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/facebook/facebook-webhook.provider.ts:66

    }
    response.status(200).send('EVENT_RECEIVED')
  }

  private handleChallenge(request: Request, response: Response): void {
    const {
      'hub.mode': mode,
      'hub.verify_token': verifyToken,
      'hub.challenge': challenge,
    } = request.query as FacebookWebhookChallengeQuery
    if (
      mode === 'subscribe'
      && verifyToken === this.config.webhookVerifyToken
      && challenge
    ) {
      response.status(200).send(challenge)
      return
    }
    response.status(403).send(getCodeMessage(ResponseCode.ChannelWebhookInvalidVerifyToken, undefined, getLocale()))
  }

  private parseMetaBody(request: Request): FacebookWebhookBody {
    const parsed = FacebookWebhookBodySchema.safeParse(request.body)
    if (!parsed.success) {
      this.logger.warn({ platform: AccountType.Facebook }, 'Facebook webhook body invalid')
      return {}
    }
    return parsed.data
  }

  private async applyFacebookChange(change: FacebookWebhookChange): Promise<void> {
    if (change.field !== FacebookWebhookField.Feed || change.value.comment_id) {
      this.logger.log({ platform: AccountType.Facebook }, 'Facebook webhook event ignored')
      return
    }
    if (!this.publishRecordRepo || !this.stateService) {
      return

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Copy the exact webhookVerifyToken from server config into the Meta App dashboard webhook verification field (or vice versa)
  2. Redeploy/restart the server so the current env value is loaded, confirming the right environment is running
  3. Re-attempt subscription in the Meta dashboard after aligning the token
  4. Ensure hub.challenge is passed through; do not manually test without all three hub params

Example fix

// before
// token hardcoded/drifted between dashboard and env
const WEBHOOK_VERIFY_TOKEN = 'old-token'
// after
const WEBHOOK_VERIFY_TOKEN = process.env.FACEBOOK_WEBHOOK_VERIFY_TOKEN
if (!WEBHOOK_VERIFY_TOKEN)
  throw new Error('FACEBOOK_WEBHOOK_VERIFY_TOKEN is required')
Defensive patterns

Strategy: validation

Validate before calling

if (req.query['hub.mode'] === 'subscribe' && req.query['hub.verify_token'] !== WEBHOOK_VERIFY_TOKEN)
  return res.status(403).send('ChannelWebhookInvalidVerifyToken')

Type guard

function isChallengeValid(req: Request, token: string): boolean {
  return req.query['hub.mode'] === 'subscribe'
    && req.query['hub.verify_token'] === token
    && typeof req.query['hub.challenge'] === 'string'
    && req.query['hub.challenge'].length > 0
}

Try / catch

app.get('/webhooks/facebook', (req, res) => {
  if (isChallengeValid(req, process.env.FACEBOOK_WEBHOOK_VERIFY_TOKEN!)) {
    return res.status(200).send(req.query['hub.challenge'])
  }
  res.status(403).send('ChannelWebhookInvalidVerifyToken')
})

Prevention

When it happens

Trigger: Meta calls GET /webhooks/facebook?hub.mode=subscribe&hub.verify_token=...&hub.challenge=... while the verify_token differs from the server's configured webhookVerifyToken, or hub.challenge is absent.

Common situations: Verify token entered in the Meta App dashboard differs from the server env value; token rotated on one side only; deploying the wrong environment (cn vs intl config) that holds a different token; URL queried manually for testing with a wrong token.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/b47183fc5b11ff8f. Report an issue: GitHub.