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
- Set the exact same verify token in Meta App Dashboard and in the app's webhookVerifyToken config
- Redeploy/restart after changing the token env var
- Confirm the callback URL registered in Meta points at this endpoint
- 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
- Use the identical verify token string in Meta dashboard and env config
- Avoid whitespace/quoting drift in env values
- Test the handshake after every token change
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
- ChannelWebhookInvalidSignature
- ChannelWebhookInvalidVerifyToken
- ChannelWebhookInvalidSignature
- ChannelWebhookInvalidVerifyToken
- InvalidAiTaskId
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/ba50e749d4dc5496.
Report an issue: GitHub.