jackwener/OpenCLI · error · CommandExecutionError

${context} returned messages from the wrong Discord target.

Error message

${context} returned messages from the wrong Discord target. Expected channel/thread id ${expectedChannelId}, saw ${actualChannelId}.

What it means

This is the final cross-check in assertDiscordMessageRowsBelongToTarget: a row's channel_id must equal the expected target thread_id/channel_id. A mismatch proves the rendered message list belongs to a different channel than requested, so the results are rejected with CommandExecutionError.

Source

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

export function assertDiscordMessageRowsBelongToTarget(rows, target, context = 'Discord read') {
    if (!target) return rows;
    const expectedChannelId = String(target.thread_id || target.channel_id || '');
    if (!expectedChannelId) {
        throw new CommandExecutionError(`${context} target is missing a stable channel/thread id.`);
    }
    for (const row of rows) {
        if (!row || typeof row !== 'object' || Array.isArray(row)) {
            throw new CommandExecutionError(`${context} returned malformed message rows.`);
        }
        const actualChannelId = row.channel_id == null || row.channel_id === '' ? '' : String(row.channel_id);
        if (!actualChannelId) {
            throw new CommandExecutionError(
                `${context} could not verify returned message target.`,
                `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;
    }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Wait for the route to settle before reading: call waitForDiscordRoute and/or waitForDiscordContent before harvesting messages.
  2. Verify the resolved target ids match what you intended ("opencli discord-app channels -f json").
  3. Re-run the command — transient navigation races usually resolve on retry.
  4. Navigate explicitly via --url with the full channel URL instead of name-based resolution.

Example fix

// before
await resolveDiscordChannelTarget(page, kwargs);
const rows = await scrapeMessages(page); // raced navigation
// after
const target = await resolveDiscordChannelTarget(page, kwargs, { required: true });
await waitForDiscordRoute(page, target);
await waitForDiscordContent(page, 'messages');
const rows = assertDiscordMessageRowsBelongToTarget(await scrapeMessages(page), target);
Defensive patterns

Strategy: retry

Validate before calling

const expected = String(target.thread_id || target.channel_id || '');
if (rows.some(r => r && String(r.channel_id ?? '') !== expected)) throw new Error('Rows channel mismatch before verification');

Type guard

null

Try / catch

try { rows = assertDiscordMessageRowsBelongToTarget(rows, target, ctx); } catch (e) { if (String(e.message).includes('wrong Discord target')) { await waitForDiscordRoute(page, target); await waitForDiscordContent(page, 'messages'); rows = assertDiscordMessageRowsBelongToTarget(await scrapeMessages(page), target, ctx); } else throw e; }

Prevention

When it happens

Trigger: Discord actually rendered a different channel than the one resolved (stale navigation, redirected route, pinned/last-active channel) and the scraper harvested rows whose channel_id differs from expectedChannelId.

Common situations: Race between navigation and message harvesting; the app redirected to a default channel after a failed deep link; clicking behavior landing on a nearby channel; cached DOM from the previously opened channel.

Related errors


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