jackwener/OpenCLI · error · CommandExecutionError
LinkedIn thread-snapshot blocked: thread_url_mismatch
Error message
LinkedIn thread-snapshot blocked: thread_url_mismatch
What it means
The linkedin thread-snapshot command opens the given messaging thread URL and extracts the URL the browser actually landed on via an in-page discovery script. If both the requested and discovered URLs are present and canonicalize to different threads, the command refuses to snapshot the wrong conversation and throws this CommandExecutionError.
Source
Thrown at clis/linkedin/thread-snapshot.js:344
discovery = {
...retried,
scrollAttempts: firstDiscovery.scrollAttempts,
scrollStable: firstDiscovery.scrollStable,
};
} else {
discovery = retried;
}
}
if (discovery?.authRequired) {
throw new AuthRequiredError(LINKEDIN_DOMAIN, 'LinkedIn thread-snapshot requires an active signed-in LinkedIn browser session.');
}
if (!discovery || typeof discovery !== 'object' || Array.isArray(discovery) || !Array.isArray(discovery.apiUrls)) {
throw new CommandExecutionError('LinkedIn thread-snapshot returned a malformed API discovery payload.');
}
const actualUrl = canonicalizeLinkedInThreadUrl(discovery.url || '');
if (threadUrl && actualUrl && threadUrl !== actualUrl) {
throw new CommandExecutionError('LinkedIn thread-snapshot blocked: thread_url_mismatch', `Expected ${threadUrl}; actual ${actualUrl}`);
}
if (maxScrolls >= 4 && discovery.scrollAttempts >= maxScrolls && discovery.scrollStable === false) {
throw new CommandExecutionError('LinkedIn thread history did not stabilize before --max-scrolls; refusing to return a partial snapshot.');
}
const apiUrls = validateThreadApiUrls(discovery.apiUrls, threadUrl);
const csrf = await requireLinkedInCookie(page, 'LinkedIn thread-snapshot');
const fetched = unwrapEvaluateResult(
await page.evaluate(buildFetchThreadPagesScript(apiUrls, csrf)),
);
if (fetched?.authRequired) {
throw new AuthRequiredError(LINKEDIN_DOMAIN, `LinkedIn messengerMessages API authentication failed: ${fetched.error}`);
}
if (!fetched || fetched.error || !Array.isArray(fetched.pages)) {
throw new CommandExecutionError(`LinkedIn messengerMessages API returned an unexpected response: ${fetched?.error || 'no data'}`);
}
const parsed = parseThreadPages(fetched.pages);View on GitHub (pinned to 49907e53dc)
Solutions
- Re-open LinkedIn Messaging in the browser, click the intended thread, and copy the exact URL from the address bar
- Canonicalize the URL the same way the CLI does (strip query params/fragments) and compare before invoking
- Check whether the original thread was deleted or archived and re-run with the new thread URL
Example fix
// before linkedin thread-snapshot --thread-url 'https://www.linkedin.com/messaging/thread/old-id/' // after linkedin thread-snapshot --thread-url 'https://www.linkedin.com/messaging/thread/current-id/'
Defensive patterns
Strategy: validation
Validate before calling
const canon = u => u.split('?')[0].split('#')[0].replace(/\/$/,'');
if (canon(requestedUrl) !== canon(actualThreadUrl)) throw new Error('thread URL mismatch before calling thread-snapshot'); Type guard
const isSameThread = (a, b) => !!a && !!b && a.split('?')[0].replace(/\/$/,'') === b.split('?')[0].replace(/\/$/,''); Try / catch
try {
await threadSnapshot({ threadUrl });
} catch (e) {
if (String(e.message).includes('thread_url_mismatch')) {
// re-copy the URL from the browser and retry once
} else throw e;
} Prevention
- Copy thread URLs directly from the LinkedIn messenger address bar
- Strip query strings/fragments before comparing URLs
- Re-copy the URL after archiving/deleting conversations
- Never reuse URLs saved long ago without re-verifying in the browser
When it happens
Trigger: Calling `linkedin thread-snapshot --thread-url <url>` where the page's discovered thread URL (discovery.url) canonicalizes to a different thread than the requested threadUrl — typically when the thread was deleted, redirected, or the URL was typo'd/copied stale.
Common situations: Reusing an old thread URL after LinkedIn redirected the conversation; pasting a share link instead of the messenger thread URL; copy-paste errors truncating or altering the thread ID; LinkedIn redirecting to a different conversation after archiving/deleting the original.
Related errors
- LinkedIn company URL must point to linkedin.com
- LinkedIn company URL must look like /company/<name>
- LinkedIn company URL has a malformed company slug: ${m[1]}
- LinkedIn company name has unexpected characters: ${slug}
- LinkedIn search returned an unexpected response
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/bb9ffccfdb9c130f.
Report an issue: GitHub.