From 368edeaaffcd3c9bb5596fd018d4878a7397b391 Mon Sep 17 00:00:00 2001 From: ckohen Date: Fri, 29 Aug 2025 03:33:17 -0700 Subject: [PATCH] fix(rest): emit warning on HTTP 401 with non-zero code (#11066) * fix(rest): emit warning on HTTP 401 with non-zero code * fix: typos Co-authored-by: Almeida * fix: explicit globalThis --------- Co-authored-by: Almeida Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- packages/rest/src/lib/handlers/Shared.ts | 22 +++++++++++++++++++--- packages/rest/src/lib/utils/types.ts | 4 +++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/rest/src/lib/handlers/Shared.ts b/packages/rest/src/lib/handlers/Shared.ts index 7944f73db..1e29bf3a9 100644 --- a/packages/rest/src/lib/handlers/Shared.ts +++ b/packages/rest/src/lib/handlers/Shared.ts @@ -7,6 +7,8 @@ import { RESTEvents } from '../utils/constants.js'; import type { ResponseLike, HandlerRequestData, RouteData } from '../utils/types.js'; import { parseResponse, shouldRetry } from '../utils/utils.js'; +let authFalseWarningEmitted = false; + /** * Invalid request limiting is done on a per-IP basis, not a per-token basis. * The best we can do is track invalid counts process-wide (on the theory that @@ -137,15 +139,29 @@ export async function handleErrors( } else { // Handle possible malformed requests if (status >= 400 && status < 500) { + // The request will not succeed for some reason, parse the error returned from the api + const data = (await parseResponse(res)) as DiscordErrorData | OAuthErrorData; + const isDiscordError = 'code' in data; + // If we receive this status code, it means the token we had is no longer valid. if (status === 401 && requestData.auth === true) { + if (isDiscordError && data.code !== 0 && !authFalseWarningEmitted) { + const errorText = `Encountered HTTP 401 with error ${data.code}: ${data.message}. Your token will be removed from this REST instance. If you are using @discordjs/rest directly, consider adding 'auth: false' to the request. Open an issue with your library if not.`; + // Use emitWarning if possible, probably not available in edge / web + if (typeof globalThis.process !== 'undefined' && typeof globalThis.process.emitWarning === 'function') { + globalThis.process.emitWarning(errorText); + } else { + console.warn(errorText); + } + + authFalseWarningEmitted = true; + } + manager.setToken(null!); } - // The request will not succeed for some reason, parse the error returned from the api - const data = (await parseResponse(res)) as DiscordErrorData | OAuthErrorData; // throw the API error - throw new DiscordAPIError(data, 'code' in data ? data.code : data.error, status, method, url, requestData); + throw new DiscordAPIError(data, isDiscordError ? data.code : data.error, status, method, url, requestData); } return res; diff --git a/packages/rest/src/lib/utils/types.ts b/packages/rest/src/lib/utils/types.ts index 4ceba0392..40363d719 100644 --- a/packages/rest/src/lib/utils/types.ts +++ b/packages/rest/src/lib/utils/types.ts @@ -292,7 +292,9 @@ export interface RequestData { */ appendToFormData?: boolean; /** - * Alternate authorization data to use for this request only, or `false` to disable the Authorization header + * Alternate authorization data to use for this request only, or `false` to disable the Authorization header. + * When making a request to a route that includes a token (such as interactions or webhooks), set to `false` + * to avoid accidentally unsetting the instance token if a 401 is encountered. * * @defaultValue `true` */