diff --git a/packages/rest/src/lib/errors/RateLimitError.ts b/packages/rest/src/lib/errors/RateLimitError.ts index 9f8045eac..305256d61 100644 --- a/packages/rest/src/lib/errors/RateLimitError.ts +++ b/packages/rest/src/lib/errors/RateLimitError.ts @@ -21,6 +21,8 @@ export class RateLimitError extends Error implements RateLimitData { public sublimitTimeout: number; + public scope: RateLimitData['scope']; + public constructor({ timeToReset, limit, @@ -32,6 +34,7 @@ export class RateLimitError extends Error implements RateLimitData { global, retryAfter, sublimitTimeout, + scope, }: RateLimitData) { super(); this.timeToReset = timeToReset; @@ -44,6 +47,7 @@ export class RateLimitError extends Error implements RateLimitData { this.global = global; this.retryAfter = retryAfter; this.sublimitTimeout = sublimitTimeout; + this.scope = scope; } /** diff --git a/packages/rest/src/lib/handlers/BurstHandler.ts b/packages/rest/src/lib/handlers/BurstHandler.ts index 835739807..bed42999c 100644 --- a/packages/rest/src/lib/handlers/BurstHandler.ts +++ b/packages/rest/src/lib/handlers/BurstHandler.ts @@ -2,7 +2,7 @@ import type { RequestInit } from 'undici'; import type { REST } from '../REST.js'; import type { IHandler } from '../interfaces/Handler.js'; import { RESTEvents } from '../utils/constants.js'; -import type { ResponseLike, HandlerRequestData, RouteData } from '../utils/types.js'; +import type { ResponseLike, HandlerRequestData, RouteData, RateLimitData } from '../utils/types.js'; import { onRateLimit, sleep } from '../utils/utils.js'; import { handleErrors, incrementInvalidCount, makeNetworkRequest } from './Shared.js'; @@ -102,6 +102,7 @@ export class BurstHandler implements IHandler { } else if (status === 429) { // Unexpected ratelimit const isGlobal = res.headers.has('X-RateLimit-Global'); + const scope = (res.headers.get('X-RateLimit-Scope') ?? 'user') as RateLimitData['scope']; await onRateLimit(this.manager, { global: isGlobal, @@ -114,6 +115,7 @@ export class BurstHandler implements IHandler { timeToReset: retryAfter, retryAfter, sublimitTimeout: 0, + scope, }); this.debug( @@ -128,6 +130,7 @@ export class BurstHandler implements IHandler { ` Limit : ${Number.POSITIVE_INFINITY}`, ` Retry After : ${retryAfter}ms`, ` Sublimit : None`, + ` Scope : ${scope}`, ].join('\n'), ); diff --git a/packages/rest/src/lib/handlers/SequentialHandler.ts b/packages/rest/src/lib/handlers/SequentialHandler.ts index da9aefa8c..9472d8a6c 100644 --- a/packages/rest/src/lib/handlers/SequentialHandler.ts +++ b/packages/rest/src/lib/handlers/SequentialHandler.ts @@ -237,6 +237,7 @@ export class SequentialHandler implements IHandler { timeToReset: timeout, retryAfter: timeout, sublimitTimeout: 0, + scope: 'user', }; // Let library users know they have hit a rate limit @@ -281,6 +282,7 @@ export class SequentialHandler implements IHandler { const reset = res.headers.get('X-RateLimit-Reset-After'); const hash = res.headers.get('X-RateLimit-Bucket'); const retry = res.headers.get('Retry-After'); + const scope = (res.headers.get('X-RateLimit-Scope') ?? 'user') as RateLimitData['scope']; // Update the total number of requests that can be made before the rate limit resets this.limit = limit ? Number(limit) : Number.POSITIVE_INFINITY; @@ -359,6 +361,7 @@ export class SequentialHandler implements IHandler { timeToReset: timeout, retryAfter, sublimitTimeout: sublimitTimeout ?? 0, + scope, }); this.debug( @@ -373,6 +376,7 @@ export class SequentialHandler implements IHandler { ` Limit : ${limit}`, ` Retry After : ${retryAfter}ms`, ` Sublimit : ${sublimitTimeout ? `${sublimitTimeout}ms` : 'None'}`, + ` Scope : ${scope}`, ].join('\n'), ); diff --git a/packages/rest/src/lib/utils/types.ts b/packages/rest/src/lib/utils/types.ts index fa9ea0337..0b204a01d 100644 --- a/packages/rest/src/lib/utils/types.ts +++ b/packages/rest/src/lib/utils/types.ts @@ -164,6 +164,13 @@ export interface RateLimitData { * The route being hit in this request */ route: string; + /** + * The scope of the rate limit that was hit. + * + * This can be `user` for rate limits that are per client, `global` for rate limits that affect all clients or `shared` for rate limits that + * are shared per resource. + */ + scope: 'global' | 'shared' | 'user'; /** * The time, in milliseconds, that will need to pass before the sublimit lock for the route resets, and requests that fall under a sublimit * can be retried