Discord: handle early gateway startup errors

This commit is contained in:
Theo Tarr
2026-02-22 13:43:06 -05:00
committed by Peter Steinberger
parent 9b81a53016
commit 7af6849c2f
5 changed files with 205 additions and 36 deletions

View File

@@ -34,6 +34,7 @@ import { createDiscordRetryRunner } from "../../infra/retry-policy.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { createNonExitingRuntime, type RuntimeEnv } from "../../runtime.js";
import { resolveDiscordAccount } from "../accounts.js";
import { getDiscordGatewayEmitter } from "../monitor.gateway.js";
import { fetchDiscordApplicationId } from "../probe.js";
import { normalizeDiscordToken } from "../token.js";
import { createDiscordVoiceCommand } from "../voice/command.js";
@@ -229,6 +230,33 @@ function isDiscordDisallowedIntentsError(err: unknown): boolean {
return message.includes(String(DISCORD_DISALLOWED_INTENTS_CODE));
}
type EarlyGatewayErrorGuard = {
pendingErrors: unknown[];
release: () => void;
};
function attachEarlyGatewayErrorGuard(client: Client): EarlyGatewayErrorGuard {
const pendingErrors: unknown[] = [];
const gateway = client.getPlugin<GatewayPlugin>("gateway");
const emitter = getDiscordGatewayEmitter(gateway);
if (!emitter) {
return {
pendingErrors,
release: () => {},
};
}
const onGatewayError = (err: unknown) => {
pendingErrors.push(err);
};
emitter.on("error", onGatewayError);
return {
pendingErrors,
release: () => {
emitter.removeListener("error", onGatewayError);
},
};
}
export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
const cfg = opts.config ?? loadConfig();
const account = resolveDiscordAccount({
@@ -365,6 +393,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
})
: createNoopThreadBindingManager(account.accountId);
let lifecycleStarted = false;
let releaseEarlyGatewayErrorGuard = () => {};
try {
const commands: BaseCommand[] = commandSpecs.map((spec) =>
createDiscordNativeCommand({
@@ -496,6 +525,8 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
},
clientPlugins,
);
const earlyGatewayErrorGuard = attachEarlyGatewayErrorGuard(client);
releaseEarlyGatewayErrorGuard = earlyGatewayErrorGuard.release;
await deployDiscordCommands({ client, runtime, enabled: nativeEnabled });
@@ -612,8 +643,11 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
voiceManagerRef,
execApprovalsHandler,
threadBindings,
pendingGatewayErrors: earlyGatewayErrorGuard.pendingErrors,
releaseEarlyGatewayErrorGuard,
});
} finally {
releaseEarlyGatewayErrorGuard();
if (!lifecycleStarted) {
threadBindings.stop();
}