can1357/oh-my-pi · error
Discord response did not include a message ID
Error message
Discord response did not include a message ID
What it means
This error is thrown by parseMessage() in the Discord webhook blob uploader when the JSON body returned by Discord's execute-webhook endpoint (called with ?wait=true) parses successfully but has no string `id` field. The uploader needs the message ID both as `remoteId` for the publication record and to build the DELETE URL used to later remove the message/blob. It indicates the response was not a normal Discord message object — typically an error payload, an empty body, or a non-wait response shape.
Source
Thrown at packages/coding-agent/src/blob-broker/uploaders-discord.ts:56
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");
if (signedExpiry && /^[0-9a-f]+$/i.test(signedExpiry)) {
const seconds = Number.parseInt(signedExpiry, 16);View on GitHub (pinned to 9690622007)
Solutions
- Confirm the webhook request URL includes ?wait=true (the uploader sets this; verify no custom fetch/proxy strips the query string).
- Log the raw response body from the webhook POST to see what Discord actually returned (likely an error object).
- Verify the webhook URL credential is valid and the webhook still exists (a deleted webhook can yield unexpected JSON from intermediaries).
- Retry the upload; transient Discord API issues can produce malformed responses.
- Check for a man-in-the-middle proxy/enterprise TLS inspection altering the response and bypass it.
Example fix
// debugging before the parse call in upload()
const raw = await response.json();
console.log("discord webhook response:", JSON.stringify(raw));
const message = parseMessage(raw); Defensive patterns
Strategy: type-guard
Type guard
function isDiscordMessage(v: unknown): v is { id: string; attachments: unknown[] } {
return !!v && typeof v === "object" && typeof (v as Record<string, unknown>).id === "string"
&& Array.isArray((v as Record<string, unknown>).attachments);
} Try / catch
try {
const message = parseMessage(await response.json());
} catch (err) {
if (err instanceof Error && err.message.includes("message ID")) {
// log raw body and retry or fall back to another destination
}
throw err;
} Prevention
- Always execute webhooks with ?wait=true so Discord returns the full message object.
- Log raw response bodies when a destination upload fails to speed diagnosis.
- Validate webhook credentials before configuring the discord destination.
- Avoid proxies/fetch wrappers that alter response bodies for API traffic.
When it happens
Trigger: POST to https://discord.com/api/v10/webhooks/<id>/<token>?wait=true succeeded at the HTTP layer (expectOk passed) but response.json() yielded an object whose `id` is missing or not a string — e.g. Discord returned {"error": ...}, an empty object, or the request hit a proxy returning a different JSON shape; also when `wait` was stripped by an intermediary so Discord returns 204/empty body serialized oddly.
Common situations: Corporate proxies or custom fetch wrappers rewriting Discord responses; Discord API incidents returning degraded payloads; misconfigured thread_id causing a different response shape; a webhook token pointing at an endpoint that returns JSON error bodies with a 200-level status via a gateway/proxy; stale Discord API version behavior differences (v10 without wait semantics).
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
- Discord attachment did not include a URL
- Discord response did not include an attachment
- xAI device-code response missing or invalid required fields.
- ${label} missing expires_in
- Discord webhook credential is not a valid URL
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/ebd366205705c113.
Report an issue: GitHub.