jackwener/OpenCLI · error · CommandExecutionError

twitter_likes_repeated_cursor

twitter_likes_repeated_cursor

Error message

twitter_likes_repeated_cursor: archive completion cannot be proven; resume state was retained

What it means

CommandExecutionError (code twitter_likes_repeated_cursor) thrown during pagination when the API returns the same bottom cursor twice in a row without the page being provably complete. Because completion cannot be proven, the library aborts and deliberately retains the resume file so a later run can retry from the last good cursor instead of marking the archive done.

Source

Thrown at clis/twitter/likes.js:344

                allTweets.push(...tweets);
            }
            const pageComplete = !nextCursor;
            writeResumeFile(resumeFile, {
                cursor: pageComplete ? null : nextCursor,
                count: useOutputFile ? outputCount : allTweets.length,
                tweets: useOutputFile ? undefined : allTweets,
                updatedAt: new Date().toISOString(),
                complete: pageComplete,
                source: 'likes',
                username,
                outputFile: useOutputFile ? outputFile : null,
            });
            if (pageComplete) {
                exhausted = true;
                break;
            }
            if (nextCursor === cursor) {
                throw new CommandExecutionError('twitter_likes_repeated_cursor: archive completion cannot be proven; resume state was retained');
            }
            cursor = nextCursor;
        }
        const finalCount = useOutputFile ? outputCount : allTweets.length;
        if (finalCount === 0) {
            if (looksLikePrivateTwitterTimeline(lastRawResponse)) {
                throw new EmptyResultError('twitter likes', `No likes returned for @${username} (Likes are private by default on X; only the account owner can view their own likes)`);
            }
            throw new EmptyResultError('twitter likes', `No likes found for @${username}`);
        }
        // Resume is only removed after the timeline is truly exhausted. Hitting
        // --max-pages, partial API errors after some rows, or an interrupt must
        // leave the resume file so the next run can continue.
        if (exhausted)
            removeResumeFile(resumeFile);
        if (useOutputFile) {
            return {
                outputFile,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command with --resume: it continues from the retained cursor, often succeeding on retry.
  2. Wait a few minutes (transient Twitter flakiness) then resume.
  3. Re-authenticate if the session degraded mid-run, then resume.
  4. If it persists on every attempt, treat the archive as complete-at-last-stable-page or update query IDs and retry.

Example fix

// before
$ cli twitter likes @user --all  # aborted: repeated cursor
// after
$ cli twitter likes @user --all --resume  # retry from retained cursor
Defensive patterns

Strategy: retry

Validate before calling

// track cursors yourself and abort early on repetition
let lastCursor = null;
if (nextCursor && nextCursor === lastCursor) {
  await sleep(5000); // transient flakiness guard before resuming
}

Type guard

null

Try / catch

try {
  await cli.twitter.likes({ username, all: true, resume: true });
} catch (e) {
  if (e.code === 'twitter_likes_repeated_cursor') {
    await sleep(backoff); // resume state was retained; retry continues from cursor
    return retryWithResume();
  }
  throw e;
}

Prevention

When it happens

Trigger: In the pagination loop at clis/twitter/likes.js:344, pageComplete is false and nextCursor === cursor (the same cursor value returned by two consecutive Likes pages).

Common situations: Twitter serving stale/cached timeline pages; flaky API responses mid-pagination; Twitter backend bug returning the cursor but no new entries; intermittent auth degradation during a long --all fetch.

Related errors


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