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

  1. Confirm the webhook request URL includes ?wait=true (the uploader sets this; verify no custom fetch/proxy strips the query string).
  2. Log the raw response body from the webhook POST to see what Discord actually returned (likely an error object).
  3. Verify the webhook URL credential is valid and the webhook still exists (a deleted webhook can yield unexpected JSON from intermediaries).
  4. Retry the upload; transient Discord API issues can produce malformed responses.
  5. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/ebd366205705c113. Report an issue: GitHub.