jackwener/OpenCLI · warning · EmptyResultError

@${username} has no recent tweets

Error message

@${username} has no recent tweets

What it means

EmptyResultError (code EMPTY_RESULT) is thrown by `twitter tweets` after pagination loops over the user timeline and collects zero tweets. The library throws it so scripts get a distinct exit code instead of an empty table, with a hint that the account may be private or suspended. If the account is accessible, it means the scraper parsed no tweet nodes from any fetched page.

Source

Thrown at clis/twitter/tweets.js:145

        const all = [];
        let cursor = null;
        // Runaway guard only; --limit and cursor exhaustion control normal pagination.
        for (let i = 0; i < MAX_USER_TWEETS_PAGES && all.length < limit; i++) {
            if (i > 0 && pageDelaySeconds > 0) {
                await page.wait(pageDelaySeconds);
            }
            const fetchCount = Math.min(USER_TWEETS_PAGE_SIZE, limit - all.length + 10);
            const data = await fetchUserTimelinePage(page, context, cursor, fetchCount);
            if (data?.error) {
                if (all.length === 0) throw new CommandExecutionError(describeTwitterApiError('UserTweets', data.error));
                break;
            }
            const { tweets, nextCursor } = parseUserTweets(data, seen);
            all.push(...tweets);
            if (!nextCursor || nextCursor === cursor) break;
            cursor = nextCursor;
        }
        if (all.length === 0) throw new EmptyResultError(`@${username} has no recent tweets`, 'Account may be private or suspended');
        return applyTopByEngagement(all.slice(0, limit), kwargs['top-by-engagement']);
    },
});

export const __test__ = {
    MAX_TWEETS_LIMIT,
    sanitizeQueryId,
    buildUserTweetsUrl,
    buildUserByScreenNameUrl,
    extractTweet,
    parseUserTweets,
    normalizeLimit,
    normalizePageDelaySeconds,
};

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log in to X in the browser session so private/follow-gated timelines are visible, then retry
  2. Verify the account exists and is public (open https://x.com/<username> manually); skip suspended/private accounts
  3. If the account should have tweets, suspect a parser/X markup change — check for library updates or report the issue
  4. Retry later if rate-limited (empty payloads can be a soft-block symptom)

Example fix

// before
await cli.run(['twitter', 'tweets', 'someprivateuser']);
// after
try {
  await cli.run(['twitter', 'tweets', 'someprivateuser']);
} catch (e) {
  if (e.code === 'EMPTY_RESULT') console.error('Account private/suspended or no tweets');
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling
class EmptyResultError extends Error {}
const isPrivateOrSuspended = (username) =>
  fetch(`https://x.com/${username}`).then(r => r.status !== 200);

Type guard

function isEmptyResultError(e) { return e && e.code === 'EMPTY_RESULT'; }

Try / catch

try {
  const tweets = await getTweets(username);
} catch (e) {
  if (e.code === 'EMPTY_RESULT') {
    // account private, suspended, or no tweets — skip or warn
    console.warn(`@${username}: no recent tweets (private/suspended?)`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `twitter tweets <username>` when every parseUserTweets pass over the timeline API returns an empty tweets array, so `all.length === 0` after the pagination loop breaks (no nextCursor or cursor stalls).

Common situations: Target account is protected/private or suspended (logged-out or unauthenticated scraping sees nothing); account has genuinely no tweets; X changed its timeline markup so the parser matches zero nodes; heavy rate-limiting returns empty payloads.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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