can1357/oh-my-pi · error
Discord returned an invalid message response
Error message
Discord returned an invalid message response
What it means
After uploading the file via the Discord webhook (with ?wait=true so Discord returns the created message), the uploader validates the response JSON: it must be an object containing a message `id` string and a non-empty `attachments` array. A body that is not an object fails this first check, meaning Discord did not return the expected message resource.
Source
Thrown at packages/coding-agent/src/blob-broker/uploaders-discord.ts:54
throw new Error("Discord webhook credential must use HTTPS");
}
const segments = url.pathname.split("/").filter(Boolean);
const webhooksIndex = segments.indexOf("webhooks");
const id = webhooksIndex >= 0 ? segments[webhooksIndex + 1] : undefined;
const token = webhooksIndex >= 0 ? segments[webhooksIndex + 2] : undefined;
if (!id || !token || !/^\d+$/.test(id)) {
throw new Error("Discord webhook credential does not contain a webhook ID and token");
}
return { id, token };
}
function webhookEndpoint(webhook: DiscordWebhook, suffix?: string): URL {
const base = `${DISCORD_API_ORIGIN}/api/v10/webhooks/${encodeURIComponent(webhook.id)}/${encodeURIComponent(webhook.token)}`;
return new URL(suffix ? `${base}/${suffix}` : base);
}
function parseMessage(value: unknown): DiscordMessage {
if (!value || typeof value !== "object") throw new Error("Discord returned an invalid message response");
const message = value as Record<string, unknown>;
if (typeof message.id !== "string") throw new Error("Discord response did not include a message ID");
if (!Array.isArray(message.attachments)) throw new Error("Discord response did not include an attachment");
const first = message.attachments[0];
if (!first || typeof first !== "object") throw new Error("Discord response did not include an attachment");
const attachmentUrl = (first as Record<string, unknown>).url;
if (typeof attachmentUrl !== "string") throw new Error("Discord attachment did not include a URL");
try {
const parsed = new URL(attachmentUrl);
if (parsed.protocol !== "https:") throw new Error();
} catch {
throw new Error("Discord attachment URL is invalid");
}
return { id: message.id, attachmentUrl };
}
function attachmentExpiry(url: string, now: number): number {
const signedExpiry = new URL(url).searchParams.get("ex");View on GitHub (pinned to 9690622007)
Solutions
- Check the raw response body and status from the webhook POST to see what Discord actually returned
- Ensure no proxy or DNS override (e.g. for censored regions) is injecting HTML challenge pages for discord.com
- Retry the upload — transient gateway errors can return non-JSON bodies
- Verify webhook validity via GET /api/v10/webhooks/<id>/<token> and that `wait=true` is honored for your webhook type
Example fix
// before (mock returns a bare JSON array)
[{ "id": "1" }]
// after (return the message object Discord sends)
{ "id": "9999", "attachments": [{ "id": 0, "url": "https://cdn.discordapp.com/attachments/.../file.txt" }] } Defensive patterns
Strategy: try-catch
Type guard
function isDiscordMessage(v) {
return typeof v === 'object' && v !== null &&
typeof v.id === 'string' &&
Array.isArray(v.attachments) && v.attachments.length > 0 &&
typeof v.attachments[0]?.url === 'string';
} Try / catch
try {
const body = await response.json();
if (!isDiscordMessage(body)) {
throw new Error(`Discord webhook returned unexpected body: ${JSON.stringify(body).slice(0, 200)}`);
}
} catch (err) {
logger.error('Discord webhook upload failed or response malformed', { cause: err });
// retry once or fall back to another destination
} Prevention
- Keep ?wait=true so Discord returns the full message object
- Retry uploads on transient gateway/proxy failures
- Log non-JSON or malformed bodies to detect proxies injecting challenge pages
- Verify the webhook is valid (GET the webhook endpoint) before relying on it in production
When it happens
Trigger: POST to /api/v10/webhooks/<id>/<token>?wait=true returned 2xx but the body is not a JSON object — an HTML error/interstitial page from a proxy, a JSON array, `null`, or an empty body being decoded into a non-object value.
Common situations: Corporate proxies or Cloudflare challenge pages intercepting discord.com; a stubbed Discord endpoint in tests returning an incomplete body; Discord API changes; hitting a rate-limit/gateway response that still parsed to a non-object.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Gemini Files API ${context} response is not valid JSON
- Pushbullet upload fields were missing from the destination r
- Discord webhook credential is not a valid URL
- Discord webhook credential must use HTTPS
- Discord webhook credential does not contain a webhook ID and
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/683cf976ba274978.
Report an issue: GitHub.