yikart/AiToEarn · error

ChannelWebhookInvalidVerifyToken

Error message

ChannelWebhookInvalidVerifyToken

What it means

During Meta's GET subscription handshake (hub.mode=subscribe), the provider echoes hub.challenge only when hub.verify_token equals config.webhookVerifyToken. On mismatch it responds 403 with ChannelWebhookInvalidVerifyToken, telling Meta the endpoint could not be validated.

Source

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

    }
    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 InstagramWebhookChallengeQuery
    if (
      mode === 'subscribe'
      && verifyToken === this.config.webhookVerifyToken
      && challenge
    ) {
      response.status(200).send(challenge)
      return
    }
    response.status(403).send(getCodeMessage(ResponseCode.ChannelWebhookInvalidVerifyToken, undefined, getLocale()))
  }

  private verify(request: RawBodyRequest): boolean {
    const rawBody = request.rawBody
    const signature = this.getHeader(request, 'x-hub-signature-256')
    if (!signature?.startsWith('sha256=') || !rawBody || !this.config.clientSecret) {
      return false
    }

    const expected = `sha256=${createHmac('sha256', this.config.clientSecret).update(rawBody).digest('hex')}`
    return this.safeEqual(signature, expected)
  }

  private parseMetaBody(request: Request): InstagramWebhookBody | null {
    const result = InstagramWebhookBodySchema.safeParse(request.body)
    if (!result.success) {
      this.logger.warn(
        { platform: AccountType.Instagram, issues: result.error.issues },

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Set the exact same verify token in Meta App Dashboard and in the app's webhookVerifyToken config
  2. Redeploy/restart after changing the token env var
  3. Confirm the callback URL registered in Meta points at this endpoint
  4. Trim quotes/whitespace from the token env value

Example fix

// before
WEBHOOK_VERIFY_TOKEN="my token "   // trailing space
// after
WEBHOOK_VERIFY_TOKEN=my-precise-token  // matches dashboard value exactly
Defensive patterns

Strategy: validation

Validate before calling

const mode = query['hub.mode']; const token = query['hub.verify_token']; const challenge = query['hub.challenge']
if (mode !== 'subscribe' || typeof token !== 'string' || token !== process.env.INSTAGRAM_VERIFY_TOKEN || !challenge) {
  throw new Error('verify token mismatch')
}

Type guard

function isValidChallenge(q: unknown): q is { 'hub.mode': string; 'hub.verify_token': string; 'hub.challenge': string } {
  const o = q as Record<string, unknown>
  return typeof o['hub.verify_token'] === 'string' && typeof o['hub.challenge'] === 'string'
}

Prevention

When it happens

Trigger: GET webhook verification request where query.hub.verify_token is absent or does not equal this.config.webhookVerifyToken, or hub.challenge is missing.

Common situations: Verify token typed incorrectly in Meta App Dashboard vs env var; token rotated in one place only; subscribing the wrong callback URL; whitespace/quoting issues in the env value.

Related errors


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