can1357/oh-my-pi · error
Discord attachment did not include a URL
Error message
Discord attachment did not include a URL
What it means
Thrown by parseMessage() when Discord's response has a message id and a non-empty attachments array, but attachments[0].url is missing or not a string. The uploader needs this CDN URL as the publication URL for the uploaded blob. Discord message attachment objects always carry a `url` in normal operation, so its absence signals an abnormal or hostile response shape.
Source
Thrown at packages/coding-agent/src/blob-broker/uploaders-discord.ts:61
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);
const expiresAt = seconds * 1_000;
if (Number.isSafeInteger(expiresAt) && expiresAt > 0) return expiresAt;
}
return now + FALLBACK_LIFETIME_MS;
}View on GitHub (pinned to 9690622007)
Solutions
- Log the full attachments[0] object from the response to see which fields Discord returned.
- Verify requests go directly to https://discord.com/api/v10 with no reshaping proxy.
- Pin/verify the API version (v10) and check Discord's changelog for attachment schema changes.
- Retry the upload; partial records are often transient.
- If persistently reproducible, capture the response and report/check Discord status.
Defensive patterns
Strategy: type-guard
Type guard
function attachmentHasUrl(v: unknown): v is { url: string } {
return !!v && typeof v === "object" && typeof (v as Record<string, unknown>).url === "string";
} Try / catch
try {
const message = parseMessage(await response.json());
} catch (err) {
if (err instanceof Error && err.message.includes("did not include a URL")) {
// capture attachments[0] for diagnosis; fall back to another destination or retry
}
throw err;
} Prevention
- Route Discord API traffic directly to discord.com without reshaping proxies.
- Pin the Discord API version (v10) and watch for schema changes.
- Log full attachment objects when parsing fails to spot partial records.
- Retry transient failures before surfacing errors to users.
When it happens
Trigger: attachments[0] is an object lacking a `url` string field — e.g. a partial attachment record from a degraded API response, a proxy returning trimmed JSON, or Discord schema changes in preview/experimental API versions.
Common situations: Using an unofficial proxy of the Discord API that strips fields; Discord schema drift; attaching via a thread the uploader cannot fully resolve; intercepting fetch wrappers that cache and reshape responses.
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 response did not include a message ID
- 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/7395f26921a83fa5.
Report an issue: GitHub.