jackwener/OpenCLI · error · EmptyResultError
twitter download @${username}
Error message
twitter download @${username} What it means
If UserByScreenName returns no GraphQL errors but data.user.result.rest_id is still missing, the code throws EmptyResultError titled `twitter download @<username>` with detail 'Could not resolve @<username>'. This means Twitter's response was structurally valid but contained no user result payload.
Source
Thrown at clis/twitter/download.js:379
});
const ubsUrl = buildUserByScreenNameUrl(userByScreenNameOperation, username);
const userLookup = requireFetchPayload(await page.evaluate(`async () => {
try {
const resp = await fetch("${ubsUrl}", { headers: ${headers}, credentials: 'include' });
if (!resp.ok) return { ok: false, status: resp.status };
const payload = await resp.json();
return { ok: true, payload };
} catch (err) {
return { ok: false, error: err?.message ?? String(err) };
}
}`));
const normalizedUserLookup = normalizeTwitterGraphqlPayload(userLookup);
if (Array.isArray(normalizedUserLookup?.errors) && normalizedUserLookup.errors.length > 0) {
throw new CommandExecutionError(`Twitter UserByScreenName returned GraphQL errors: ${JSON.stringify(normalizedUserLookup.errors).slice(0, 200)}`);
}
const userId = normalizedUserLookup?.data?.user?.result?.rest_id;
if (!userId) throw new EmptyResultError(`twitter download @${username}`, `Could not resolve @${username}`);
const seen = new Set();
const all = [];
let cursor = null;
let hasMorePages = false;
for (let i = 0; i < MAX_PAGINATION_PAGES && all.length < limit; i++) {
const fetchCount = nextUserMediaFetchCount(limit, all.length);
if (fetchCount === 0) break;
const url = buildUserMediaUrl(userMediaOperation, userId, fetchCount, cursor);
const data = normalizeTwitterGraphqlPayload(requireFetchPayload(await page.evaluate(`async () => {
try {
const r = await fetch("${url}", { headers: ${headers}, credentials: 'include' });
if (!r.ok) return { ok: false, status: r.status };
return { ok: true, payload: await r.json() };
} catch (err) {
return { ok: false, error: err?.message ?? String(err) };
}
}`)));View on GitHub (pinned to 49907e53dc)
Solutions
- Confirm the account is public and viewable at https://x.com/<username>
- Update the CLI to match the current UserByScreenName response shape (rest_id path)
- Retry later in case of transient server-side truncation
Defensive patterns
Strategy: type-guard
Validate before calling
const userId = normalized?.data?.user?.result?.rest_id;
if (!userId) {
console.warn('Could not resolve user; verify the account is public');
} Type guard
const hasResolvedUser = (payload) => typeof payload?.data?.user?.result?.rest_id === 'string';
Try / catch
try {
await cmd();
} catch (err) {
if (err.name === 'EmptyResultError' && err.message.includes('Could not resolve')) {
// treat as 'user unresolvable' — verify account visibility
}
} Prevention
- Verify the account is public and resolvable at x.com/<username>
- Update the CLI when x.com changes the rest_id response path
- Handle both EmptyResultError and GraphQL-error cases when scripting
When it happens
Trigger: UserByScreenName returns an empty/partial data node: protected or deleted account edge cases, response shape changes after x.com updates, or responses where user.result lacks rest_id (e.g. some restricted states).
Common situations: x.com changed the GraphQL response shape so rest_id moved or was renamed; the account exists but its result is withheld in your region; caching/stale operation metadata producing a truncated response.
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
- ${probe.detail}
- No bookmarks found for the logged-in account
- Timeline is private or unavailable to the current X account;
- twitter_collection_request_error
- device_follow
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/70ece77911289cf3.
Report an issue: GitHub.