{"record":{"id":"97739c717bb39607","repo":"jackwener/OpenCLI","slug":"username-has-no-recent-tweets","errorCode":null,"errorMessage":"@${username} has no recent tweets","messagePattern":"@(.+?) has no recent tweets","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"warning","filePath":"clis/twitter/tweets.js","lineNumber":145,"sourceCode":"        const all = [];\n        let cursor = null;\n        // Runaway guard only; --limit and cursor exhaustion control normal pagination.\n        for (let i = 0; i < MAX_USER_TWEETS_PAGES && all.length < limit; i++) {\n            if (i > 0 && pageDelaySeconds > 0) {\n                await page.wait(pageDelaySeconds);\n            }\n            const fetchCount = Math.min(USER_TWEETS_PAGE_SIZE, limit - all.length + 10);\n            const data = await fetchUserTimelinePage(page, context, cursor, fetchCount);\n            if (data?.error) {\n                if (all.length === 0) throw new CommandExecutionError(describeTwitterApiError('UserTweets', data.error));\n                break;\n            }\n            const { tweets, nextCursor } = parseUserTweets(data, seen);\n            all.push(...tweets);\n            if (!nextCursor || nextCursor === cursor) break;\n            cursor = nextCursor;\n        }\n        if (all.length === 0) throw new EmptyResultError(`@${username} has no recent tweets`, 'Account may be private or suspended');\n        return applyTopByEngagement(all.slice(0, limit), kwargs['top-by-engagement']);\n    },\n});\n\nexport const __test__ = {\n    MAX_TWEETS_LIMIT,\n    sanitizeQueryId,\n    buildUserTweetsUrl,\n    buildUserByScreenNameUrl,\n    extractTweet,\n    parseUserTweets,\n    normalizeLimit,\n    normalizePageDelaySeconds,\n};\n","sourceCodeStart":127,"sourceCodeEnd":160,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/tweets.js#L127-L160","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Log in to X in the browser session so private/follow-gated timelines are visible, then retry","Verify the account exists and is public (open https://x.com/<username> manually); skip suspended/private accounts","If the account should have tweets, suspect a parser/X markup change — check for library updates or report the issue","Retry later if rate-limited (empty payloads can be a soft-block symptom)"],"exampleFix":"// before\nawait cli.run(['twitter', 'tweets', 'someprivateuser']);\n// after\ntry {\n  await cli.run(['twitter', 'tweets', 'someprivateuser']);\n} catch (e) {\n  if (e.code === 'EMPTY_RESULT') console.error('Account private/suspended or no tweets');\n}","handlingStrategy":"try-catch","validationCode":"// before calling\nclass EmptyResultError extends Error {}\nconst isPrivateOrSuspended = (username) =>\n  fetch(`https://x.com/${username}`).then(r => r.status !== 200);","typeGuard":"function isEmptyResultError(e) { return e && e.code === 'EMPTY_RESULT'; }","tryCatchPattern":"try {\n  const tweets = await getTweets(username);\n} catch (e) {\n  if (e.code === 'EMPTY_RESULT') {\n    // account private, suspended, or no tweets — skip or warn\n    console.warn(`@${username}: no recent tweets (private/suspended?)`);\n  } else throw e;\n}","preventionTips":["Only request timelines for accounts you know are public","Log in to x.com in the browser session before fetching, so protected timelines resolve","Handle EMPTY_RESULT as a normal 'skip this account' branch in batch scripts","Back off and retry later on rate-limited batch runs"],"tags":["twitter","empty-result","scraping","authentication"],"backgroundTag":"empty-result-set","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}