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
- Collect the webhook URL or envelope before saving the channel
- 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)