jackwener/OpenCLI · error · ArgumentError

--url must be a Discord thread/post URL like https://discord

Error message

--url must be a Discord thread/post URL like https://discord.com/channels/<guild_id>/<channel_id>/<thread_id>.

What it means

resolveDiscordThreadTarget is the thread/post counterpart of the channel resolver: it parses --url and requires that the parsed result contain a thread_id (a 3-segment URL). A plain 2-segment channel URL parses fine but has no thread_id, so it throws this ArgumentError directing you to the thread URL form.

Source

Thrown at clis/discord-app/utils.js:624

                `Expected channel/thread id ${expectedChannelId}, but the message row did not include channel_id.`,
            );
        }
        if (actualChannelId && actualChannelId !== expectedChannelId) {
            throw new CommandExecutionError(
                `${context} returned messages from the wrong Discord target.`,
                `Expected channel/thread id ${expectedChannelId}, saw ${actualChannelId}.`,
            );
        }
    }
    return rows;
}

export async function resolveDiscordThreadTarget(page, kwargs = {}) {
    const urlArg = stringArg(kwargs.url);
    if (urlArg) {
        const parsed = parseDiscordChannelUrl(urlArg);
        if (!parsed || !parsed.thread_id) {
            throw new ArgumentError('--url must be a Discord thread/post URL like https://discord.com/channels/<guild_id>/<channel_id>/<thread_id>.');
        }
        return parsed;
    }

    const threadArg = stringArg(kwargs.thread);
    if (!threadArg) {
        throw new ArgumentError('Pass --thread <thread_id> or --url <thread_url>.');
    }

    const parsedThreadUrl = parseDiscordChannelUrl(threadArg);
    if (parsedThreadUrl?.thread_id) return parsedThreadUrl;

    const channelTarget = hasDiscordChannelTarget(kwargs)
        ? await resolveDiscordChannelTarget(page, kwargs, { required: true })
        : null;
    if (channelTarget) {
        return {
            guild_id: channelTarget.guild_id,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Use the full thread URL https://discord.com/channels/<guild_id>/<channel_id>/<thread_id> (open the thread/post in Discord and use Copy Link).
  2. If you actually want the parent channel, use resolveDiscordChannelTarget / the channel-target command instead.
  3. Pass --thread <name_or_id> (with guild context) instead of --url.
  4. Verify the URL has three numeric segments after /channels/.

Example fix

// before
await resolveDiscordThreadTarget(page, { url: 'https://discord.com/channels/123/456' }); // channel URL
// after
await resolveDiscordThreadTarget(page, { url: 'https://discord.com/channels/123/456/789' }); // thread URL
Defensive patterns

Strategy: validation

Validate before calling

function isThreadUrl(u) { return /^https:\/\/discord\.com\/channels\/\d+\/\d+\/\d+$/.test(String(u||'').trim()); }
if (!isThreadUrl(opts.url)) throw new Error('Expected a 3-segment Discord thread/post URL');

Type guard

function isDiscordThreadUrl(v) { return typeof v === 'string' && /^https:\/\/discord\.com\/channels\/\d+\/\d+\/\d+$/.test(v.trim()); }

Try / catch

try { const target = await resolveDiscordThreadTarget(page, { url }); } catch (e) { if (String(e.message).includes('--url must be a Discord thread/post URL')) { // route to channel resolver or ask user for the thread link
} else throw e; }

Prevention

When it happens

Trigger: Calling resolveDiscordThreadTarget (via the target command) with kwargs.url set to a channel URL (https://discord.com/channels/<guild>/<channel>) or an unparseable string — anything whose parsed.thread_id is falsy.

Common situations: Copying a channel link instead of opening a thread and copying its link; using a forum channel URL instead of an individual post URL; typos that drop the third URL segment.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/ba2553a8fb27790c. Report an issue: GitHub.