koala73/worldmonitor · error · ConvexError

webhookEnvelope required for webhook channel

Error message

webhookEnvelope required for webhook channel

What it means

Channel-shape validation in setChannel: channelType is 'webhook' but args.webhookEnvelope is missing. Generic webhook channels persist the webhook envelope as their payload, so it is required to construct the channel document.

Solutions

  1. Collect the webhook URL or envelope before saving the channel
  2. Require the webhookEnvelope field in the UI when webhook is the selected channel type
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at convex/notificationChannels.ts:517 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at convex/notificationChannels.ts:550

      throw new ConvexError("telegram channel must be linked through bot pairing");
    } 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") {
      const email = requireVerifiedAccountEmail(args.email, identity.emailVerified === true ? identity.email : undefined);
      const doc = { userId, channelType: "email" as const, email, emailOwnership: "verified_account" as const, verified: true, linkedAt: now };
      if (existing) {
        await ctx.db.replace(existing._id, doc);
      } else {
        await ctx.db.insert("notificationChannels", doc);
      }
    } else if (args.channelType === "webhook") {
      if (!args.webhookEnvelope) throw new ConvexError("webhookEnvelope required for webhook channel");
      const doc = { userId, channelType: "webhook" as const, webhookEnvelope: args.webhookEnvelope, verified: true, linkedAt: now, webhookLabel: args.webhookLabel };
      if (existing) {
        await ctx.db.replace(existing._id, doc);
      } else {
        await ctx.db.insert("notificationChannels", doc);
      }
    } else {
      throw new ConvexError("discord channel must be set via set-discord-oauth");
    }
  },
});

// Called by the notification relay via /relay/deactivate HTTP action
// when Telegram returns 403 or Slack returns 404/410. Ungated by design —
// delivery-side deactivation must work after Pro lapses (see #8430).
export const deactivateChannelForUser = internalMutation({
  args: { userId: v.string(), channelType: channelTypeValidator },
  handler: async (ctx, args) => {

View on GitHub (pinned to e586b8b4b8)