koala73/worldmonitor · error · ConvexError

telegram channel must be linked through bot pairing

telegram channel must be linked through bot pairing

Error message

telegram channel must be linked through bot pairing

What it means

The notification-channel linking mutation rejects channelType "telegram" outright: Telegram channels cannot be created by supplying a webhook/token like Slack or email. They may only be established through the bot-pairing flow (user links via the bot), so this code path treats any telegram insert/update request as unsupported.

Solutions

  1. Use the Telegram bot pairing flow (/start with the pairing code) instead of this mutation.
  2. Remove 'telegram' from client-side channel-type options for this direct-linking path.
  3. If a Telegram channel must be created programmatically, route through the bot-pairing internal mutation that owns that flow.

Example fix

// before
await api.notificationChannels.link({ channelType: "telegram", telegramChatId: "123" });
// after
// pair via the bot, then use the pairing endpoint
await api.notificationChannels.completeBotPairing({ pairingCode: code });
Defensive patterns

Strategy: validation

Validate before calling

if (channelType === "telegram") {
  throw new Error("use the Telegram bot pairing flow; direct linking is not supported");
}

Try / catch

try {
  await linkChannel({ channelType });
} catch (e) {
  if (String(e?.message).includes("bot pairing")) {
    startTelegramBotPairing();
  }
}

Prevention

When it happens

Trigger: Calling the link-notification-channel mutation with channelType "telegram" and expecting it to create or update a notificationChannels document directly.

Common situations: Client UI or script migrated from a generic channel-linking form to Telegram; developer tries to seed a Telegram channel server-side; a stale client still offers 'Telegram' in its channel-type dropdown.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/732c66db2d95d626. Report an issue: GitHub.

Appendix: source

Thrown at convex/notificationChannels.ts:224

    email: v.optional(v.string()),
    webhookLabel: v.optional(v.string()),
    scheduleWelcome: v.optional(v.boolean()),
    // Internal-only: derived by the relay HTTP handler from Clerk, never the body.
    verifiedAccountEmail: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const { userId, channelType, webhookEnvelope, email, webhookLabel } = args;
    const existing = await ctx.db
      .query("notificationChannels")
      .withIndex("by_user_channel", (q) =>
        q.eq("userId", userId).eq("channelType", channelType),
      )
      .unique();
    const isNew = !existing;
    let channelId = existing ? String(existing._id) : "";
    const now = Date.now();
    if (channelType === "telegram") {
      throw new ConvexError("telegram channel must be linked through bot pairing");
    } else if (channelType === "slack") {
      if (!webhookEnvelope) throw new ConvexError("webhookEnvelope required for slack channel");
      const doc = { userId, channelType: "slack" as const, webhookEnvelope, verified: true, linkedAt: now };
      if (existing) { await ctx.db.replace(existing._id, doc); } else { channelId = String(await ctx.db.insert("notificationChannels", doc)); }
    } else if (channelType === "email") {
      await assertProEntitlement(ctx, userId);
      const recipient = requireVerifiedAccountEmail(email, args.verifiedAccountEmail);
      const doc = { userId, channelType: "email" as const, email: recipient, emailOwnership: "verified_account" as const, verified: true, linkedAt: now };
      if (existing) { await ctx.db.replace(existing._id, doc); } else { channelId = String(await ctx.db.insert("notificationChannels", doc)); }
    } else if (channelType === "webhook") {
      if (!webhookEnvelope) throw new ConvexError("webhookEnvelope required for webhook channel");
      const doc = { userId, channelType: "webhook" as const, webhookEnvelope, verified: true, linkedAt: now, webhookLabel };
      if (existing) { await ctx.db.replace(existing._id, doc); } else { channelId = String(await ctx.db.insert("notificationChannels", doc)); }
    } else {
      throw new ConvexError("discord channel must be set via set-discord-oauth");
    }
    if (isNew && args.scheduleWelcome === true) {
      await ctx.scheduler.runAfter(

View on GitHub (pinned to 7d06c8633d)