koala73/worldmonitor · error · ConvexError
discord channel must be set via set-discord-oauth
Error message
discord channel must be set via set-discord-oauth
What it means
Thrown by the `setChannel` Convex mutation as the terminal `else` branch: `channelType` is neither `telegram`, `slack`, `email`, nor `webhook`. Discord channels are intentionally NOT supported here — they must be established through the dedicated `set-discord-oauth` OAuth flow, which stores Discord tokens rather than a static envelope. Plain string-typed ConvexError.
Source
Thrown at convex/notificationChannels.ts:525
}
} 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) {
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");
}
},
});
export const deleteChannel = mutation({
args: { channelType: channelTypeValidator },
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();View on GitHub (pinned to ffec79ac33)
Solutions
- Route Discord setup through the `set-discord-oauth` flow instead of `setChannel`.
- In the UI, branch on channelType: send `discord` to the OAuth entrypoint and everything else to `setChannel`.
- Remove `discord` from the list of options that target `setChannel`.
Example fix
// before
await setChannel({ channelType: "discord" });
// after
// Discord is linked via OAuth, not setChannel:
window.location.href = discordOAuthStartUrl; Defensive patterns
Strategy: validation
Validate before calling
if (channelType === "discord") {
// Discord must go through OAuth, not setChannel
startDiscordOAuth();
return;
}
await setChannel({ channelType, ...rest }); Type guard
const SETTABLE_CHANNELS = new Set(["telegram", "slack", "email", "webhook"]);
function isSettableChannel(t: string): boolean {
return SETTABLE_CHANNELS.has(t);
} Prevention
- Branch the UI so discord never reaches setChannel.
- Remove discord from the options list that maps to setChannel.
When it happens
Trigger: Calling `setChannel({ channelType: "discord", ... })`. Any client that lets a user pick "discord" from the channel list and routes it into `setChannel` instead of the Discord OAuth handler.
Common situations: A unified channel-picker UI that sends every channel type to the same mutation; stale client code that predates the dedicated Discord OAuth flow; a typo/hardcoded value producing an unknown channelType.
Related errors
- INCOMPATIBLE_DELIVERY
- COUNTRIES_LIMIT_EXCEEDED
- TICKERS_LIMIT_EXCEEDED
- digestHour must be an integer 0–23
- digestTimezone must be a valid IANA timezone (e.g. America/N
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/83010dc54d89fc79.
Report an issue: GitHub.