paperclipai/paperclip · error · Error
Discord ${operation} failed (HTTP ${response.status}${code ?
Error message
Discord ${operation} failed (HTTP ${response.status}${code ? `, code ${code}` : ""}${invalidFields.length > 0 ? `, invalid fields: ${invalidFields.join(", ")}` : ""}) What it means
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.
Source
Thrown at server/src/services/chat-discord.ts:148
});
let body: unknown;
try {
body = await response.json();
} catch {
throw new Error("Discord returned an unreadable response");
}
if (!response.ok) {
const errorBody =
body && typeof body === "object" ? (body as DiscordErrorBody) : null;
const codeValue = String(errorBody?.code ?? "");
const code = /^\d{1,10}$/.test(codeValue) ? codeValue : null;
const invalidFields =
errorBody?.errors && typeof errorBody.errors === "object"
? Object.keys(errorBody.errors)
.filter((field) => SAFE_DISCORD_ERROR_FIELDS.has(field))
.slice(0, 8)
: [];
throw new Error(
`Discord ${operation} failed (HTTP ${response.status}${code ? `, code ${code}` : ""}${invalidFields.length > 0 ? `, invalid fields: ${invalidFields.join(", ")}` : ""})`,
);
}
return body as T;
}
function channelPermissions(input: {
channel: DiscordChannel;
guildId: string;
memberId: string;
memberRoleIds: Set<string>;
roles: DiscordRole[];
}): bigint {
let permissions = 0n;
for (const role of input.roles) {
if (role.id === input.guildId || input.memberRoleIds.has(role.id ?? "")) {
permissions |= bigint(role.permissions);
}View on GitHub (pinned to 01ad858492)
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
Example fix
// before
await discordJson(fetchGuild, "server lookup"); // raw failure surfaces
// after
try {
await discordJson(fetchGuild, "server lookup");
} catch (err) {
if (/HTTP 429/.test(err.message)) await sleep(getRetryAfter(err));
else if (/HTTP 401/.test(err.message)) throw new ConfigError("Discord bot token invalid");
else throw err;
} Defensive patterns
Strategy: try-catch
Try / catch
try {
body = await discordJson(fetchFn, operation);
} catch (err) {
const m = err.message.match(/HTTP (\d{3})(?:, code (\d+))?/);
if (m && m[1] === "429") return retryAfter(getRetryAfterMs(err));
if (m && m[1] === "401") throw new ConfigError("Discord bot token invalid or revoked");
if (m && (m[1] === "403" || m[1] === "404")) throw new ConfigError("Check bot permissions and IDs");
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- OpenCode API ${path} request failed: ${redact(String(error),
- OpenCode API ${path} returned HTTP ${response.status}: ${red
- request_failed
- Discord returned an unreadable response
- ${(payload as { error?: string } | null)?.error ?? `Failed t
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/c2bbcbcf7ab805ac.
Report an issue: GitHub.