{"record":{"id":"bb9ffccfdb9c130f","repo":"jackwener/OpenCLI","slug":"linkedin-thread-snapshot-blocked-thread-url-misma","errorCode":null,"errorMessage":"LinkedIn thread-snapshot blocked: thread_url_mismatch","messagePattern":"LinkedIn thread-snapshot blocked: thread_url_mismatch","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/thread-snapshot.js","lineNumber":344,"sourceCode":"        discovery = {\n          ...retried,\n          scrollAttempts: firstDiscovery.scrollAttempts,\n          scrollStable: firstDiscovery.scrollStable,\n        };\n      } else {\n        discovery = retried;\n      }\n    }\n    if (discovery?.authRequired) {\n      throw new AuthRequiredError(LINKEDIN_DOMAIN, 'LinkedIn thread-snapshot requires an active signed-in LinkedIn browser session.');\n    }\n    if (!discovery || typeof discovery !== 'object' || Array.isArray(discovery) || !Array.isArray(discovery.apiUrls)) {\n      throw new CommandExecutionError('LinkedIn thread-snapshot returned a malformed API discovery payload.');\n    }\n\n    const actualUrl = canonicalizeLinkedInThreadUrl(discovery.url || '');\n    if (threadUrl && actualUrl && threadUrl !== actualUrl) {\n      throw new CommandExecutionError('LinkedIn thread-snapshot blocked: thread_url_mismatch', `Expected ${threadUrl}; actual ${actualUrl}`);\n    }\n    if (maxScrolls >= 4 && discovery.scrollAttempts >= maxScrolls && discovery.scrollStable === false) {\n      throw new CommandExecutionError('LinkedIn thread history did not stabilize before --max-scrolls; refusing to return a partial snapshot.');\n    }\n\n    const apiUrls = validateThreadApiUrls(discovery.apiUrls, threadUrl);\n    const csrf = await requireLinkedInCookie(page, 'LinkedIn thread-snapshot');\n    const fetched = unwrapEvaluateResult(\n      await page.evaluate(buildFetchThreadPagesScript(apiUrls, csrf)),\n    );\n    if (fetched?.authRequired) {\n      throw new AuthRequiredError(LINKEDIN_DOMAIN, `LinkedIn messengerMessages API authentication failed: ${fetched.error}`);\n    }\n    if (!fetched || fetched.error || !Array.isArray(fetched.pages)) {\n      throw new CommandExecutionError(`LinkedIn messengerMessages API returned an unexpected response: ${fetched?.error || 'no data'}`);\n    }\n\n    const parsed = parseThreadPages(fetched.pages);","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/thread-snapshot.js#L326-L362","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlinkedin thread-snapshot --thread-url 'https://www.linkedin.com/messaging/thread/old-id/'\n// after\nlinkedin thread-snapshot --thread-url 'https://www.linkedin.com/messaging/thread/current-id/'","handlingStrategy":"validation","validationCode":"const canon = u => u.split('?')[0].split('#')[0].replace(/\\/$/,'');\nif (canon(requestedUrl) !== canon(actualThreadUrl)) throw new Error('thread URL mismatch before calling thread-snapshot');","typeGuard":"const isSameThread = (a, b) => !!a && !!b && a.split('?')[0].replace(/\\/$/,'') === b.split('?')[0].replace(/\\/$/,'');","tryCatchPattern":"try {\n  await threadSnapshot({ threadUrl });\n} catch (e) {\n  if (String(e.message).includes('thread_url_mismatch')) {\n    // re-copy the URL from the browser and retry once\n  } else throw e;\n}","preventionTips":["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"],"tags":["linkedin","url-validation","browser-automation"],"backgroundTag":"url-mismatch","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}