{"record":{"id":"c2bbcbcf7ab805ac","repo":"paperclipai/paperclip","slug":"discord-operation-failed-http-response-statu","errorCode":null,"errorMessage":"Discord ${operation} failed (HTTP ${response.status}${code ? `, code ${code}` : \"\"}${invalidFields.length > 0 ? `, invalid fields: ${invalidFields.join(\", \")}` : \"\"})","messagePattern":"Discord (.+?) failed \\(HTTP (.+?)(.+?)` : \"\"\\}(.+?)` : \"\"\\}\\)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-discord.ts","lineNumber":148,"sourceCode":"  });\n  let body: unknown;\n  try {\n    body = await response.json();\n  } catch {\n    throw new Error(\"Discord returned an unreadable response\");\n  }\n  if (!response.ok) {\n    const errorBody =\n      body && typeof body === \"object\" ? (body as DiscordErrorBody) : null;\n    const codeValue = String(errorBody?.code ?? \"\");\n    const code = /^\\d{1,10}$/.test(codeValue) ? codeValue : null;\n    const invalidFields =\n      errorBody?.errors && typeof errorBody.errors === \"object\"\n        ? Object.keys(errorBody.errors)\n            .filter((field) => SAFE_DISCORD_ERROR_FIELDS.has(field))\n            .slice(0, 8)\n        : [];\n    throw new Error(\n      `Discord ${operation} failed (HTTP ${response.status}${code ? `, code ${code}` : \"\"}${invalidFields.length > 0 ? `, invalid fields: ${invalidFields.join(\", \")}` : \"\"})`,\n    );\n  }\n  return body as T;\n}\n\nfunction channelPermissions(input: {\n  channel: DiscordChannel;\n  guildId: string;\n  memberId: string;\n  memberRoleIds: Set<string>;\n  roles: DiscordRole[];\n}): bigint {\n  let permissions = 0n;\n  for (const role of input.roles) {\n    if (role.id === input.guildId || input.memberRoleIds.has(role.id ?? \"\")) {\n      permissions |= bigint(role.permissions);\n    }","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-discord.ts#L130-L166","documentation":"discordJson() throws this when Discord returned a parseable body but a non-OK HTTP status. The message embeds the operation name (e.g. 'guild member lookup'), HTTP status, Discord error code, and up to 8 whitelisted invalid-field keys from the error body, giving a compact sanitized summary of why the Discord REST call failed.","triggerScenarios":"Any discordJson call ([user/application/guild] or [guild/member/roles/channels] lookups) where Discord responds 4xx/5xx: 401 for an invalid bot token, 403 for missing access, 404 for unknown IDs, 429 for rate limits, 400 with field errors for bad payloads.","commonSituations":"Expired or revoked bot token (401); bot kicked from the guild (404/403); missing intents or permissions; hitting Discord rate limits (429); malformed snowflake IDs reaching the API (400 with invalid fields).","solutions":["Read the embedded HTTP status and code: 401 -> fix the bot token, 403 -> fix permissions, 404 -> fix IDs, 429 -> add rate-limit backoff/retry-after handling","If invalid fields are listed, correct those request payload fields","Verify the bot is installed in the target guild with required scopes","Wrap calls in retry logic honoring Discord's 429 retry_after"],"exampleFix":"// before\nawait discordJson(fetchGuild, \"server lookup\"); // raw failure surfaces\n// after\ntry {\n  await discordJson(fetchGuild, \"server lookup\");\n} catch (err) {\n  if (/HTTP 429/.test(err.message)) await sleep(getRetryAfter(err));\n  else if (/HTTP 401/.test(err.message)) throw new ConfigError(\"Discord bot token invalid\");\n  else throw err;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  body = await discordJson(fetchFn, operation);\n} catch (err) {\n  const m = err.message.match(/HTTP (\\d{3})(?:, code (\\d+))?/);\n  if (m && m[1] === \"429\") return retryAfter(getRetryAfterMs(err));\n  if (m && m[1] === \"401\") throw new ConfigError(\"Discord bot token invalid or revoked\");\n  if (m && (m[1] === \"403\" || m[1] === \"404\")) throw new ConfigError(\"Check bot permissions and IDs\");\n  throw err;\n}","preventionTips":["Map each Discord HTTP status/code to a concrete remediation and log it","Honor 429 retry_after with backoff before any retry","Verify bot token, guild membership, and permissions before calling endpoints","Keep a smoke test that exercises verifyDiscordBot against a real guild in staging"],"tags":["discord","http","api","rest"],"backgroundTag":"http-error-response","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}