can1357/oh-my-pi · error

Discord attachment URL is invalid

Error message

Discord attachment URL is invalid

What it means

The Discord uploader extracts an attachment URL from a message payload and validates it before use. If the URL cannot be parsed by the URL constructor or does not use the https: protocol, this error is thrown. It guards against malformed or insecure attachment links returned by Discord.

Source

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

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);
		const expiresAt = seconds * 1_000;
		if (Number.isSafeInteger(expiresAt) && expiresAt > 0) return expiresAt;
	}
	return now + FALLBACK_LIFETIME_MS;
}

/** Create the built-in Discord webhook uploader, or `null` for another destination. */
export function createDiscordUploader(
	destination: BlobDestinationId,
	config: DestinationRuntimeConfig,

View on GitHub (pinned to 9690622007)

Solutions

  1. Log the attachment object and confirm `url` is present and a string before calling the uploader
  2. Ensure the attachment URL is absolute and starts with https:// (Discord CDN URLs normally do)
  3. If reconstructing payloads, copy `attachment.url` verbatim from the Discord API response instead of building it manually
  4. Catch this error and fall back to another uploader or surface the message payload for debugging

Example fix

// before
const attachmentUrl = attachment.url ?? `/files/${attachment.id}`;
// after
const attachmentUrl = attachment.url;
if (!attachmentUrl || !attachmentUrl.startsWith("https://")) {
  throw new Error(`attachment ${attachment.id} has no https URL`);
}
Defensive patterns

Strategy: validation

Validate before calling

function hasHttpsUrl(attachment) {
  return typeof attachment?.url === "string" && URL.canParse(attachment.url) && new URL(attachment.url).protocol === "https:";
}
if (!hasHttpsUrl(message.attachments[0])) throw new Error("attachment has no valid https URL");

Type guard

function isDiscordAttachment(value) {
  return typeof value === "object" && value !== null && typeof value.url === "string" && value.url.startsWith("https://");
}

Try / catch

try {
  await broker.publish(request);
} catch (err) {
  if (err.message === "Discord attachment URL is invalid") {
    logger.warn("Discord attachment unusable", { attachment: firstAttachment });
    return null; // or fall back to another broker
  }
  throw err;
}

Prevention

When it happens

Trigger: The first attachment in the message has no `url` string field; the value is not a valid absolute URL (e.g. relative path or garbage); or the URL parses but uses a protocol other than https (http:, cdn:, data:).

Common situations: Consuming Discord message objects from a custom or mocked payload that omits attachment.url; proxy responses rewriting attachment URLs to http; hand-built test fixtures with relative paths; Discord API version changes altering the attachment shape.

Related errors


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