jackwener/OpenCLI · error · CommandExecutionError
LinkedIn thread history did not stabilize before --max-scrol
Error message
LinkedIn thread history did not stabilize before --max-scrolls; refusing to return a partial snapshot.
What it means
thread-snapshot scrolls the thread upward to load older messages and requires scroll history to stabilize (discovery.scrollStable === true) before returning. If it consumed maxScrolls (default 30, and >= 4) attempts without the message history settling, it refuses to return a partial snapshot and throws this CommandExecutionError.
Source
Thrown at clis/linkedin/thread-snapshot.js:347
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);
const recipient = parsed.recipientNames.join(', ');
const latestMessageText = parsed.messages[parsed.messages.length - 1]?.text || '';
const normalized = {View on GitHub (pinned to 49907e53dc)
Solutions
- Increase --max-scrolls (e.g. 60 or 100) to give the thread more room to load its full history
- Improve network conditions or retry later if LinkedIn is throttling the pagination API
- Accept a truncated view only for short threads where stabilization isn't needed
- Re-run the command; scroll state is not cached so a retry starts fresh
Example fix
// before linkedin thread-snapshot --thread-url <url> --max-scrolls 10 // after linkedin thread-snapshot --thread-url <url> --max-scrolls 100
Defensive patterns
Strategy: retry
Validate before calling
// Estimate thread length first; long threads need a high max-scrolls const maxScrolls = estimatedMessageCount > 200 ? 100 : 30;
Try / catch
try {
await threadSnapshot({ threadUrl, maxScrolls });
} catch (e) {
if (String(e.message).includes('did not stabilize')) {
await threadSnapshot({ threadUrl, maxScrolls: maxScrolls * 2 }); // retry with more headroom
} else throw e;
} Prevention
- Set --max-scrolls generously for long-lived threads
- Run on a fast, stable network connection
- Avoid concurrent LinkedIn commands that could trigger pagination throttling
- Retry before accepting failure — scroll state is not cached
When it happens
Trigger: Running thread-snapshot on a very long thread where LinkedIn loads older pages lazily and scrollAttempts reaches --max-scrolls with scrollStable still false — e.g. slow network, heavy thread, or aggressive throttling of the messengerMessages pagination API.
Common situations: Snapshotting multi-year conversations with thousands of messages; slow/limited connectivity causing lazy-load requests to time out; setting --max-scrolls too low (e.g. 5) on a long thread; LinkedIn rate-limiting the pagination endpoint.
Related errors
- coupang search location evaluation failed: ${error?.message
- Sales Navigator messaging thread API returned partial histor
- ${label} must be ${range}, got ${parsed}
- LinkedIn search returned an unexpected response
- LinkedIn sent invitations returned a malformed extraction pa
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/7aba83d99c750d97.
Report an issue: GitHub.