jackwener/OpenCLI · error · CommandExecutionError

twitter_collection_repeated_cursor

twitter_collection_repeated_cursor

Error message

twitter_collection_repeated_cursor: pagination cannot prove completion

What it means

paginateCollection tracks every cursor it has seen; if the next cursor equals the current one or was already visited, X is serving a pagination loop. Since a loop means the run could cycle forever without proving it reached 'until', the library throws this error immediately.

Source

Thrown at clis/twitter/collection.js:289

            }
            const createdAt = parseCreatedAt(post);
            if (!oldestSeenAt || createdAt < oldestSeenAt) oldestSeenAt = createdAt;
            posts.push(post);
            if (createdAt <= until) {
                return {
                    posts,
                    receipt: completedReceipt('time_boundary_reached', until, pageIndex + 1, oldestSeenAt),
                };
            }
        }
        if (!nextCursor) {
            return {
                posts,
                receipt: completedReceipt('cursor_exhausted', until, pageIndex + 1, oldestSeenAt),
            };
        }
        if (nextCursor === cursor || seenCursors.has(nextCursor)) {
            throw new CommandExecutionError('twitter_collection_repeated_cursor: pagination cannot prove completion');
        }
        if (posts.length >= limit) {
            throw new CommandExecutionError('twitter_collection_limit_reached: pagination cannot prove completion');
        }
        seenCursors.add(nextCursor);
        cursor = nextCursor;
    }
    throw new CommandExecutionError('twitter_collection_page_guard_hit: pagination cannot prove completion');
}

cli({
    site: 'twitter',
    name: 'collection',
    access: 'read',
    description: 'Fetch a user timeline with relationship facts and a bounded completion receipt.',
    domain: 'x.com',
    strategy: Strategy.COOKIE,
    browser: true,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Back off and retry later — cursor loops are frequently transient under rate limiting.
  2. Refresh authentication/session; degraded sessions often get stale cursors.
  3. Stop at the last good page and record the oldest post seen, treating further history as unavailable.
  4. Update the library if X changed cursor encoding (cursor extraction no longer matches the new value format).
  5. Try a fresh browser profile to bypass server-side cache stickiness.

Example fix

// before
if (nextCursor === cursor || seenCursors.has(nextCursor)) {
  throw new CommandExecutionError('twitter_collection_repeated_cursor: ...');
}
// after
if (nextCursor === cursor || seenCursors.has(nextCursor)) {
  console.warn('cursor loop detected; returning posts collected so far');
  return { posts, receipt: completedReceipt('cursor_loop', until, pageIndex + 1, oldestSeenAt) };
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (nextCursor && seenCursors.has(nextCursor)) {
  console.warn('cursor already seen before pagination call — cursor loop likely');
}

Try / catch

try {
  await paginateCollection(...);
} catch (err) {
  if (String(err.message).startsWith('twitter_collection_repeated_cursor')) {
    // keep posts collected so far; back off and retry or stop at cursor_exhausted
  } else throw err;
}

Prevention

When it happens

Trigger: UserTweets returns the same bottom_cursor on consecutive pages — cursor state expired server-side, X degraded/rate-limited responses repeating a page, or repeated entries in timeline instructions causing the extractor to reuse a stale cursor.

Common situations: Very old timelines where X truncates history and repeats the last cursor; rate-limited sessions receiving cached pages; X experiment modes breaking bottom_cursor advancement; scraping accounts with huge gaps causing empty cursor-advancing pages.

Related errors


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