can1357/oh-my-pi · error

Discord webhook credential does not contain a webhook ID and

Error message

Discord webhook credential does not contain a webhook ID and token

What it means

After URL parsing and the HTTPS check, the uploader extracts the webhook ID and token from the path segment after `webhooks` and validates that the ID is numeric. If the path does not contain `/webhooks/<numeric-id>/<token>`, the credential is not a usable Discord webhook and this error is thrown.

Source

Thrown at packages/coding-agent/src/blob-broker/uploaders-discord.ts:43

}

function parseWebhook(value: string): DiscordWebhook {
	let url: URL;
	try {
		url = new URL(value);
	} catch {
		throw new Error("Discord webhook credential is not a valid URL");
	}

	if (url.protocol !== "https:") {
		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");

View on GitHub (pinned to 9690622007)

Solutions

  1. Use the full Copy Webhook URL: https://discord.com/api/webhooks/<numeric-id>/<token>
  2. Verify both the numeric ID and token segments are present after /webhooks/ in the URL
  3. Ensure you are not pasting a Slack or other-provider webhook URL into the discord destination
  4. Recreate the webhook in Discord if the URL was truncated or the token was rotated

Example fix

// before (channel URL, not a webhook)
webhookUrl = https://discord.com/channels/1234567890/9876543210
// after
webhookUrl = https://discord.com/api/webhooks/1234567890/aBcDeFgHiJkLmNoP
Defensive patterns

Strategy: validation

Validate before calling

function extractWebhookIdAndToken(webhookUrl) {
  const url = new URL(webhookUrl);
  const segs = url.pathname.split('/').filter(Boolean);
  const i = segs.indexOf('webhooks');
  const id = i >= 0 ? segs[i + 1] : undefined;
  const token = i >= 0 ? segs[i + 2] : undefined;
  if (!id || !token || !/^\d+$/.test(id)) {
    throw new Error('URL is not a Discord webhook (need /webhooks/<numeric-id>/<token>)');
  }
  return { id, token };
}

Type guard

function isDiscordWebhookUrl(value) {
  if (typeof value !== 'string') return false;
  try {
    const url = new URL(value);
    const segs = url.pathname.split('/').filter(Boolean);
    const i = segs.indexOf('webhooks');
    return i >= 0 && /^\d+$/.test(segs[i + 1] ?? '') && segs[i + 2] !== undefined;
  } catch {
    return false;
  }
}

Try / catch

try {
  await publishToDiscord(blob);
} catch (err) {
  if (err.message.includes('webhook ID and token')) {
    logger.error('webhookUrl is not a Discord webhook URL — re-copy from Discord Integrations');
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: The credential is a valid https URL but its path lacks `webhooks` (e.g. a Discord channel or API URL), the ID or token segment is missing, or the ID is non-numeric (e.g. a webhook created via a guild template or an interaction endpoint ` interactions/<id>/... ` URL was pasted instead).

Common situations: Pasting a Discord channel invite or API URL instead of the webhook URL; using an incoming-webhook URL from Slack (which has a different path shape) in the discord destination; truncating the URL so the token is cut off; Discord's newer `?wait=` or extra-path variants pasted partially.

Related errors


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