{"record":{"id":"2371ef8a828b9d1a","repo":"jackwener/OpenCLI","slug":"context-returned-messages-from-the-wrong-discor","errorCode":null,"errorMessage":"${context} returned messages from the wrong Discord target. Expected channel/thread id ${expectedChannelId}, saw ${actualChannelId}.","messagePattern":"(.+?) returned messages from the wrong Discord target\\. Expected channel/thread id (.+?), saw (.+?)\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/discord-app/utils.js","lineNumber":610,"sourceCode":"export function assertDiscordMessageRowsBelongToTarget(rows, target, context = 'Discord read') {\n    if (!target) return rows;\n    const expectedChannelId = String(target.thread_id || target.channel_id || '');\n    if (!expectedChannelId) {\n        throw new CommandExecutionError(`${context} target is missing a stable channel/thread id.`);\n    }\n    for (const row of rows) {\n        if (!row || typeof row !== 'object' || Array.isArray(row)) {\n            throw new CommandExecutionError(`${context} returned malformed message rows.`);\n        }\n        const actualChannelId = row.channel_id == null || row.channel_id === '' ? '' : String(row.channel_id);\n        if (!actualChannelId) {\n            throw new CommandExecutionError(\n                `${context} could not verify returned message target.`,\n                `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","sourceCodeStart":592,"sourceCodeEnd":628,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/discord-app/utils.js#L592-L628","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wait for the route to settle before reading: call waitForDiscordRoute and/or waitForDiscordContent before harvesting messages.","Verify the resolved target ids match what you intended (\"opencli discord-app channels -f json\").","Re-run the command — transient navigation races usually resolve on retry.","Navigate explicitly via --url with the full channel URL instead of name-based resolution."],"exampleFix":"// before\nawait resolveDiscordChannelTarget(page, kwargs);\nconst rows = await scrapeMessages(page); // raced navigation\n// after\nconst target = await resolveDiscordChannelTarget(page, kwargs, { required: true });\nawait waitForDiscordRoute(page, target);\nawait waitForDiscordContent(page, 'messages');\nconst rows = assertDiscordMessageRowsBelongToTarget(await scrapeMessages(page), target);","handlingStrategy":"retry","validationCode":"const expected = String(target.thread_id || target.channel_id || '');\nif (rows.some(r => r && String(r.channel_id ?? '') !== expected)) throw new Error('Rows channel mismatch before verification');","typeGuard":"null","tryCatchPattern":"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; }","preventionTips":["Always waitForDiscordRoute (and content) between resolving a target and reading messages.","Use full --url deep links to avoid redirect-to-default-channel behavior.","Discard DOM caches between channel switches; re-harvest after navigation.","Retry once on mismatch — it is usually a navigation race."],"tags":["discord","race-condition","message-verification","wrong-target"],"backgroundTag":"wrong-resource-returned","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}