{"record":{"id":"ba2553a8fb27790c","repo":"jackwener/OpenCLI","slug":"url-must-be-a-discord-thread-post-url-like-https","errorCode":null,"errorMessage":"--url must be a Discord thread/post URL like https://discord.com/channels/<guild_id>/<channel_id>/<thread_id>.","messagePattern":"--url must be a Discord thread/post URL like https://discord\\.com/channels/<guild_id>/<channel_id>/<thread_id>\\.","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/discord-app/utils.js","lineNumber":624,"sourceCode":"                `Expected channel/thread id ${expectedChannelId}, but the message row did not include channel_id.`,\n            );\n        }\n        if (actualChannelId && actualChannelId !== expectedChannelId) {\n            throw new CommandExecutionError(\n                `${context} returned messages from the wrong Discord target.`,\n                `Expected channel/thread id ${expectedChannelId}, saw ${actualChannelId}.`,\n            );\n        }\n    }\n    return rows;\n}\n\nexport async function resolveDiscordThreadTarget(page, kwargs = {}) {\n    const urlArg = stringArg(kwargs.url);\n    if (urlArg) {\n        const parsed = parseDiscordChannelUrl(urlArg);\n        if (!parsed || !parsed.thread_id) {\n            throw new ArgumentError('--url must be a Discord thread/post URL like https://discord.com/channels/<guild_id>/<channel_id>/<thread_id>.');\n        }\n        return parsed;\n    }\n\n    const threadArg = stringArg(kwargs.thread);\n    if (!threadArg) {\n        throw new ArgumentError('Pass --thread <thread_id> or --url <thread_url>.');\n    }\n\n    const parsedThreadUrl = parseDiscordChannelUrl(threadArg);\n    if (parsedThreadUrl?.thread_id) return parsedThreadUrl;\n\n    const channelTarget = hasDiscordChannelTarget(kwargs)\n        ? await resolveDiscordChannelTarget(page, kwargs, { required: true })\n        : null;\n    if (channelTarget) {\n        return {\n            guild_id: channelTarget.guild_id,","sourceCodeStart":606,"sourceCodeEnd":642,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/discord-app/utils.js#L606-L642","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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).","If you actually want the parent channel, use resolveDiscordChannelTarget / the channel-target command instead.","Pass --thread <name_or_id> (with guild context) instead of --url.","Verify the URL has three numeric segments after /channels/."],"exampleFix":"// before\nawait resolveDiscordThreadTarget(page, { url: 'https://discord.com/channels/123/456' }); // channel URL\n// after\nawait resolveDiscordThreadTarget(page, { url: 'https://discord.com/channels/123/456/789' }); // thread URL","handlingStrategy":"validation","validationCode":"function isThreadUrl(u) { return /^https:\\/\\/discord\\.com\\/channels\\/\\d+\\/\\d+\\/\\d+$/.test(String(u||'').trim()); }\nif (!isThreadUrl(opts.url)) throw new Error('Expected a 3-segment Discord thread/post URL');","typeGuard":"function isDiscordThreadUrl(v) { return typeof v === 'string' && /^https:\\/\\/discord\\.com\\/channels\\/\\d+\\/\\d+\\/\\d+$/.test(v.trim()); }","tryCatchPattern":"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\n} else throw e; }","preventionTips":["Copy the link from inside the open thread/post, not the parent channel.","Verify the URL has three numeric segments after /channels/ before calling.","If the input may be either kind, dispatch: 2 segments -> resolveDiscordChannelTarget, 3 segments -> resolveDiscordThreadTarget."],"tags":["discord","cli","argument-validation","url-parsing"],"backgroundTag":"invalid-url-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}