{"record":{"id":"cb5e2d0a0c56f8a9","repo":"affaan-m/ECC","slug":"discord-request-failed-response-status","errorCode":null,"errorMessage":"Discord request failed (${response.status})","messagePattern":"Discord request failed \\((.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/discord/release-announce.mjs","lineNumber":143,"sourceCode":"    { id: discussionId, body },\n  );\n  return data.addDiscussionComment.comment.id;\n}\n\nasync function deleteReceiptComment(commentId) {\n  await githubGraphql(\n    `mutation($id:ID!){deleteDiscussionComment(input:{id:$id}){clientMutationId}}`,\n    { id: commentId },\n  );\n}\n\nasync function discord(method, path, body) {\n  const response = await request(`https://discord.com/api/v10${path}`, {\n    method,\n    headers: { Authorization: `Bot ${env.DISCORD_BOT_TOKEN}`, 'Content-Type': 'application/json' },\n    body: body ? JSON.stringify(body) : undefined,\n  });\n  if (!response.ok) throw new Error(`Discord request failed (${response.status})`);\n  return response.status === 204 ? null : response.json();\n}\n\nasync function deliver(discussion) {\n  const key = announcementKey({ repository: env.GITHUB_REPOSITORY, discussionId: discussion.id });\n  if (env.DISCORD_ANNOUNCE_WEBHOOK_URL) {\n    if (!env.GITHUB_TOKEN) throw new Error('GitHub receipt configuration is missing');\n    const webhookUrl = normalizeDiscordWebhookUrl(env.DISCORD_ANNOUNCE_WEBHOOK_URL);\n    const marker = discussionReceiptMarker(key);\n    const existingReceipt = await findReceiptComment(discussion.id, marker);\n    if (existingReceipt) {\n      if (discussionReceiptStatus(existingReceipt) === 'complete') {\n        console.log('announcement already delivered');\n        return;\n      }\n      throw new Error('announcement has a pending receipt; inspect Discord before clearing it');\n    }\n    const claimId = await addReceiptComment(discussion.id, `${marker}\\n\\nDiscord delivery: pending.`);","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/discord/release-announce.mjs#L125-L161","documentation":"discord() is the low-level wrapper for all Discord REST v10 calls and throws this whenever `response.ok` is false, embedding the status code. Non-2xx covers 400 (bad body), 401 (bad bot token), 403 (missing permissions), 404 (unknown channel/thread), and 429 (rate limited).","triggerScenarios":"Any discord(method, path, body) call — e.g. POST to /channels/{id}/messages or threads endpoint — returning non-ok: 401 invalid DISCORD_BOT_TOKEN, 403 bot lacks Send Messages / permissions in the channel, 404 wrong channel ID, 429 rate limited, 400 malformed message body (empty content, invalid embed).","commonSituations":"Expired or reset bot token; bot not added to the server/channel; missing Message Content or Send Messages intents/permissions; announcement posted to a forum channel that only accepts threads; hitting Discord's 5 req/5s channel rate limit on bulk runs.","solutions":["Read the status: 401 -> regenerate/fix DISCORD_BOT_TOKEN; 403 -> invite bot with Send Messages permission; 404 -> fix channel ID; 429 -> honor Retry-After with backoff; 400 -> validate the message payload","Log the response body, which contains Discord's specific error codes and messages","Verify the bot is a member of the target guild and can access the channel","Add retry-with-backoff handling for 429 before surfacing the error"],"exampleFix":"// before\nif (!response.ok) throw new Error(`Discord request failed (${response.status})`);\n// after\nif (!response.ok) {\n  const detail = await response.text();\n  if (response.status === 429) {\n    const retryAfter = Number(response.headers.get('retry-after') || 1) * 1000;\n    await new Promise(r => setTimeout(r, retryAfter));\n    // retry once, then throw\n  }\n  throw new Error(`Discord request failed (${response.status}): ${detail}`);\n}","handlingStrategy":"retry","validationCode":"// validate token and channel before sending\nconst tokenRes = await fetch('https://discord.com/api/v10/users/@me', { headers: { Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN}` } });\nif (tokenRes.status === 401) throw new Error('DISCORD_BOT_TOKEN is invalid or expired');\nconst chanRes = await fetch(`https://discord.com/api/v10/channels/${process.env.DISCORD_CHANNEL_ID}`, { headers: { Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN}` } });\nif (!chanRes.ok) throw new Error(`Channel not accessible: ${chanRes.status}`);","typeGuard":null,"tryCatchPattern":"try {\n  await discord('POST', `/channels/${channelId}/messages`, { content });\n} catch (err) {\n  const m = /Discord request failed \\((\\d+)\\)/.exec(err.message);\n  if (m && m[1] === '429') {\n    await sleep(retryAfterMs); // retry with backoff\n  } else if (m && ['401','403','404'].includes(m[1])) {\n    console.error('Fix bot token, permissions, or channel id — no retry will help');\n  }\n  throw err;\n}","preventionTips":["Preflight the bot token with GET /users/@me before runs","Invite the bot with Send Messages / Send Messages in Threads permissions on the target channel","Add automatic retry with Retry-After handling for 429 responses","Log Discord's JSON error body (it includes numeric error codes explaining the failure)","For forum channels, create a thread first instead of posting messages directly"],"tags":["discord-api","http","bot"],"backgroundTag":"http-error-response","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}