thedotmack/claude-mem · error · Error

Discord API error: ${response.status} - ${errorText}

Error message

Discord API error: ${response.status} - ${errorText}

What it means

postToDiscord POSTs an embed payload to the DISCORD_UPDATES_WEBHOOK URL read from .env. If Discord returns a non-2xx (4xx rate-limit/invalid-webhook/auth, 5xx Discord outage), the script reads the response body and throws a combined error. The webhook is fire-and-forget release notification; this error aborts the notification.

Source

Thrown at scripts/discord-release-notify.js:92

          },
        ],
        footer: {
          text: 'claude-mem • Persistent memory for Claude Code',
        },
        timestamp: new Date().toISOString(),
      },
    ],
  };

  const response = await fetch(webhookUrl, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Discord API error: ${response.status} - ${errorText}`);
  }

  return true;
}

async function main() {
  const version = process.argv[2];
  const customNotes = process.argv[3];

  if (!version) {
    console.error('Usage: node scripts/discord-release-notify.js <version> [notes]');
    console.error('Example: node scripts/discord-release-notify.js v7.4.2');
    process.exit(1);
  }

  console.log(`📣 Posting release notification for ${version}...`);

  const webhookUrl = loadEnv();

View on GitHub (pinned to d768ba3643)

Solutions

  1. Verify the webhook URL in .env (DISCORD_UPDATES_WEBHOOK) is current and not revoked — regenerate in Discord and re-test with curl.
  2. If the body mentions 'rate limit' (429), wait the indicated retry_after seconds and re-run; this script posts at most once per release so a one-off retry suffices.
  3. If 400 on embed size, lower the truncate maxLength in cleanNotes/truncate (currently 2000) and ensure fields stay within Discord's limits.
  4. For 5xx, re-run after a short wait — Discord outages are transient.

Example fix

// before
if (!response.ok) {
  const errorText = await response.text();
  throw new Error(`Discord API error: ${response.status} - ${errorText}`);
}

// after — handle 429 rate limit with its Retry-After header
if (!response.ok) {
  if (response.status === 429) {
    const retryAfter = parseFloat(response.headers.get('retry-after') || '1') * 1000;
    await new Promise(r => setTimeout(r, retryAfter));
    return postToDiscord(webhookUrl, version, notes); // one retry
  }
  const errorText = await response.text();
  throw new Error(`Discord API error: ${response.status} - ${errorText}`);
}
Defensive patterns

Strategy: retry

Validate before calling

// Validate the webhook shape and a cheap reachability before posting:
function looksLikeDiscordWebhook(url: string): boolean {
  return /^https:\/\/discord(?:app)?\.com\/api\/(?:webhooks|v\d+\/webhooks)\/[0-9]+\/[A-Za-z0-9_-]+$/.test(url);
}
if (!looksLikeDiscordWebhook(webhookUrl)) throw new Error('DISCORD_UPDATES_WEBHOOK is malformed');

Type guard

function isDiscordRateLimit(res: Response): boolean {
  return res.status === 429;
}

Try / catch

// main() already wraps postToDiscord in try/catch (discord-release-notify.js:117-123).
// For 429, honor Retry-After and retry once (see exampleFix). For 4xx auth/not-found,
// surface the message and exit non-zero — do not retry a revoked webhook.

Prevention

When it happens

Trigger: Webhook URL revoked or malformed (401/403/404). Rate limited (429) from posting too frequently. Payload exceeds Discord embed limits — title+description over 2000 chars after truncate, or too many fields (400). Discord 5xx outage.

Common situations: Webhook rotated in Discord channel settings and .env not updated. Re-running the script rapidly after a release (429). Notes containing content that pushes the embed over limits. Discord temporarily degraded.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/2337353f3cc2102f. Report an issue: GitHub.