koala73/worldmonitor · error · ConvexError

chatId required for telegram channel

Error message

chatId required for telegram channel

What it means

Thrown by `setChannel` when `channelType === "telegram"` but `args.chatId` is missing/empty. After auth and Pro-entitlement pass, the telegram branch requires a chatId to store. Plain-string ConvexError; `err.data === "chatId required for telegram channel"`. This is per-channel-type required-field validation — each branch (telegram/slack/email/webhook) has its own guard.

Source

Thrown at convex/notificationChannels.ts:493

    webhookLabel: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) throw new ConvexError("UNAUTHENTICATED");
    const userId = identity.subject;
    await assertProEntitlement(ctx, userId);

    const existing = await ctx.db
      .query("notificationChannels")
      .withIndex("by_user_channel", (q) =>
        q.eq("userId", userId).eq("channelType", args.channelType),
      )
      .unique();

    const now = Date.now();

    if (args.channelType === "telegram") {
      if (!args.chatId) throw new ConvexError("chatId required for telegram channel");
      const doc = { userId, channelType: "telegram" as const, chatId: args.chatId, verified: true, linkedAt: now };
      if (existing) {
        await ctx.db.replace(existing._id, doc);
      } else {
        await ctx.db.insert("notificationChannels", doc);
      }
    } else if (args.channelType === "slack") {
      if (!args.webhookEnvelope) throw new ConvexError("webhookEnvelope required for slack channel");
      const doc = { userId, channelType: "slack" as const, webhookEnvelope: args.webhookEnvelope, verified: true, linkedAt: now };
      if (existing) {
        await ctx.db.replace(existing._id, doc);
      } else {
        await ctx.db.insert("notificationChannels", doc);
      }
    } else if (args.channelType === "email") {
      if (!args.email) throw new ConvexError("email required for email channel");
      const doc = { userId, channelType: "email" as const, email: args.email, verified: true, linkedAt: now };
      if (existing) {

View on GitHub (pinned to ffec79ac33)

Solutions

  1. Capture the Telegram chat id from the bot-link callback before calling `setChannel`.
  2. Client-side: disable the save button until `chatId` is non-empty for the telegram branch.
  3. On `err.data === "chatId required for telegram channel"`, show a field-level error and re-run the bot-link flow.

Example fix

// before
await convex.mutation(api.notificationChannels.setChannel, { channelType: "telegram" });

// after
if (channelType === "telegram" && !chatId) {
  setFieldError("chatId", "Link your Telegram chat first.");
  return;
}
await convex.mutation(api.notificationChannels.setChannel, { channelType: "telegram", chatId });
Defensive patterns

Strategy: validation

Validate before calling

if (channelType === "telegram" && !chatId) {
  setFieldError("chatId", "Link your Telegram chat first.");
  return;
}

Type guard

function hasTelegramChatId(args: { channelType: string; chatId?: string }): boolean {
  return args.channelType !== "telegram" || (typeof args.chatId === "string" && args.chatId.length > 0);
}

Try / catch

try {
  await convex.mutation(api.notificationChannels.setChannel, { channelType: "telegram", chatId });
} catch (err) {
  if (err.data === "chatId required for telegram channel") setFieldError("chatId", "Link your Telegram chat first.");
  else throw err;
}

Prevention

When it happens

Trigger: Calling `setChannel({ channelType: "telegram" })` with no `chatId`; `chatId` is an empty string or undefined; the Telegram OAuth/link flow didn't yield a chat id before the mutation fired.

Common situations: The Telegram bot-link flow completed the auth step but the chat id wasn't captured; a UI form that submits the channel type before the chat id field is populated; a test calling the mutation with only the channel type.

Related errors


AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12). Data as JSON: /api/errors/a071d12540763eae. Report an issue: GitHub.