{"record":{"id":"d0fbb8ff31f3214b","repo":"discordjs/discord.js","slug":"httperror-status-res-statustext-method-url-req","errorCode":null,"errorMessage":"HTTPError(status, res.statusText, method, url, requestData)","messagePattern":"HTTPError\\(status, res\\.statusText, method, url, requestData\\)","errorType":"http","errorClass":"HTTPError","httpStatus":null,"severity":"error","filePath":"packages/rest/src/lib/handlers/Shared.ts","lineNumber":170,"sourceCode":"\tmethod: string,\n\turl: string,\n\trequestData: HandlerRequestData,\n\tretries: number,\n\trouteId: RouteData,\n) {\n\tconst status = res.status;\n\tif (status >= 500 && status < 600) {\n\t\t// Retry the specified number of times for possible server side issues\n\t\tif (retries !== manager.options.retries) {\n\t\t\tconst backoff = normalizeRetryBackoff(\n\t\t\t\tmanager.options.retryBackoff,\n\t\t\t\trouteId.bucketRoute,\n\t\t\t\tstatus,\n\t\t\t\tretries,\n\t\t\t\trequestData.body,\n\t\t\t);\n\t\t\tif (backoff === null) {\n\t\t\t\tthrow new HTTPError(status, res.statusText, method, url, requestData);\n\t\t\t}\n\n\t\t\tif (backoff > 0) {\n\t\t\t\tawait sleep(backoff);\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\t// We are out of retries, throw an error\n\t\tthrow new HTTPError(status, res.statusText, method, url, requestData);\n\t} else {\n\t\t// Handle possible malformed requests\n\t\tif (status >= 400 && status < 500) {\n\t\t\t// The request will not succeed for some reason, parse the error returned from the api\n\t\t\tconst data = (await parseResponse(res)) as DiscordErrorData | OAuthErrorData;\n\t\t\tconst isDiscordError = 'code' in data;\n","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/discordjs/discord.js/blob/a81ed8a306d37fdc746e26a634b6a42164ba2c8c/packages/rest/src/lib/handlers/Shared.ts#L152-L188","documentation":"handleErrors() in Shared.ts throws an HTTPError when Discord returns a 4xx/5xx status that could not be recovered from: for 5xx, either a retry backoff could not be computed (normalizeRetryBackoff returned null, e.g. rejectOnRateLimit/retryBackoff callback rejected) or all configured retries were exhausted. The error carries the status code, statusText, method, URL, and request body so the caller can inspect what failed.","triggerScenarios":"Discord returning 500/502/503/504 on every attempt until manager.options.retries (default 3) is exhausted; a 5xx response where the retryBackoff function (or rejectOnRateLimit-style callback) returns/rejects such that normalizeRetryBackoff yields null; any 4xx (400/401/403/404) which is never retried and immediately surfaces as an HTTPError.","commonSituations":"Discord API incidents/outages causing sustained 5xx responses; malformed request payloads producing 400; revoked or lacking permissions producing 403; deleted resources producing 404; aggressive custom retryBackoff configuration that cancels retries.","solutions":["Read error.status and the parsed error body (error.rawError / error.code) to identify the root cause — 5xx means retry later, 4xx means fix the request.","For 5xx, wrap requests in retry logic with exponential backoff and consider raising RESTOptions.retries (default 3).","For 4xx, fix the request: validate the JSON body, check route parameters exist, and ensure the bot token/permissions are valid for 401/403.","Listen to RESTEvents.Response / use the error's method, url, and requestData fields to log the exact failing request for diagnosis.","Check the Discord status page (discordstatus.com) when seeing clusters of 5xx errors."],"exampleFix":"// before\nconst user = await rest.get(Routes.user(id)); // throws HTTPError on transient 502\n// after\ntry {\n  const user = await rest.get(Routes.user(id));\n} catch (error) {\n  if (error instanceof HTTPError && error.status >= 500) {\n    await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));\n    return retry();\n  }\n  throw error;\n}","handlingStrategy":"retry","validationCode":"// Validate request shape before sending to avoid 4xx HTTPErrors\nfunction assertValidUserId(id: string) {\n  if (!/^\\d{17,20}$/.test(id)) throw new TypeError(`Invalid snowflake id: ${id}`);\n}","typeGuard":"import { HTTPError } from '@discordjs/rest';\nconst isHTTPError = (e: unknown): e is HTTPError => e instanceof HTTPError;\nconst isServerError = (e: unknown): e is HTTPError => isHTTPError(e) && e.status >= 500;","tryCatchPattern":"async function withRetry<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {\n  try {\n    return await fn();\n  } catch (error) {\n    if (isHTTPError(error) && error.status >= 500 && attempts > 0) {\n      await new Promise((r) => setTimeout(r, 1000 * 2 ** (3 - attempts)));\n      return withRetry(fn, attempts - 1);\n    }\n    throw error;\n  }\n}","preventionTips":["Distinguish 5xx (transient — retry with backoff) from 4xx (permanent — fix the request) via error.status before deciding how to respond.","Inspect error.rawError / error.code for Discord's machine-readable error code on 4xx failures.","Raise RESTOptions.retries if your workload tolerates longer waits during Discord incidents.","Log error.method, error.url, and error.requestData to reproduce the failing request.","Monitor discordstatus.com and alert on elevated 5xx rates rather than paging on single failures."],"tags":["http","network","api-error","retry-exhausted","discord-api"],"backgroundTag":"http-5xx-server-error","analyzedSha":"a81ed8a306d37fdc746e26a634b6a42164ba2c8c","analyzedAt":"2026-08-30T04:07:22.193Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}