jackwener/OpenCLI · error · CommandExecutionError
Could not resolve user @${username}
Error message
Could not resolve user @${username} What it means
listRemoveUser first resolves the target screen name to a numeric user ID via the UserByScreenName GraphQL endpoint executed inside the browser page. If the fetch fails (non-OK status), the response shape is unexpected, or rest_id is absent, it returns null and this CommandExecutionError is thrown. It means the library could not map @username to a Twitter user ID, so the removal cannot proceed.
Source
Thrown at clis/twitter/list-remove-core.js:83
const ct0 = cookies.find((c) => c.name === 'ct0')?.value || null;
if (!ct0) throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');
const userByScreenNameQueryId = await resolveTwitterQueryId(page, 'UserByScreenName', USER_BY_SCREEN_NAME_QUERY_ID);
const headers = JSON.stringify({
'Authorization': `Bearer ${decodeURIComponent(TWITTER_BEARER_TOKEN)}`,
'X-Csrf-Token': ct0,
'X-Twitter-Auth-Type': 'OAuth2Session',
'X-Twitter-Active-User': 'yes',
});
const userLookupUrl = buildUserByScreenNameQueryUrl(userByScreenNameQueryId, username);
const userId = unwrapBrowserResult(await page.evaluate(`async () => {
const resp = await fetch(${JSON.stringify(userLookupUrl)}, { headers: ${headers}, credentials: 'include' });
if (!resp.ok) return null;
const d = await resp.json();
return d.data?.user?.result?.rest_id || null;
}`));
if (!userId) throw new CommandExecutionError(`Could not resolve user @${username}`);
// Resolve listId → name so we can match the dialog row.
const listsQueryId = await resolveTwitterQueryId(page, 'ListsManagementPageTimeline', LISTS_MANAGEMENT_QUERY_ID);
const listsUrl = `/i/api/graphql/${listsQueryId}/ListsManagementPageTimeline?features=${encodeURIComponent(JSON.stringify(LISTS_MANAGEMENT_FEATURES))}`;
const listsData = unwrapBrowserResult(await page.evaluate(`async () => {
const r = await fetch(${JSON.stringify(listsUrl)}, { headers: ${headers}, credentials: 'include' });
if (!r.ok) return { __error: 'HTTP ' + r.status };
return await r.json();
}`));
if (listsData && listsData.__error) {
throw new CommandExecutionError(`Could not fetch lists: ${listsData.__error}`);
}
const parsedLists = parseListsManagement(listsData, new Set());
const targetList = parsedLists.find((l) => l.id === listId);
if (!targetList) {
throw new CommandExecutionError(`List ${listId} not found among your lists.`);
}
const targetName = targetList.name;View on GitHub (pinned to 49907e53dc)
Solutions
- Verify the username is correct and the account still exists by visiting https://x.com/<username> in a browser.
- Re-login to x.com in the controlled browser so cookies (ct0, auth_token) are fresh, then retry.
- Check whether the request is rate-limited (HTTP 429) and wait before retrying.
- Update the UserByScreenName query ID (USER_BY_SCREEN_NAME_QUERY_ID) to the current value from x.com's web app if Twitter rotated it.
- Confirm the logged-in session can view the target account (age/region blocks and withheld accounts resolve to null).
Example fix
// before
await cli.run('twitter list-remove', { listId: '12345', username: '@flwer' });
// after
await cli.run('twitter list-remove', { listId: '12345', username: '@flower' }); // corrected handle; account exists Defensive patterns
Strategy: validation
Validate before calling
// Pre-check the handle and session before invoking
const username = '@flower'.replace(/^@/, '').trim();
if (!/^[A-Za-z0-9_]{1,15}$/.test(username)) throw new Error('Invalid Twitter handle');
const cookies = await page.getCookies({ url: 'https://x.com' });
if (!cookies.some(c => c.name === 'ct0')) throw new Error('Not logged into x.com'); Type guard
function isResolvedUser(v) {
return typeof v === 'object' && v !== null && typeof v.rest_id === 'string' && /^\d+$/.test(v.rest_id);
} Try / catch
try {
await listRemoveUser(page, { listId, username });
} catch (e) {
if (e instanceof CommandExecutionError && e.message.includes('Could not resolve user')) {
// verify handle exists / refresh session, then retry
} else throw e;
} Prevention
- Validate the handle format before calling.
- Confirm the account exists by visiting its profile first.
- Keep the x.com session fresh; re-login when cookies age out.
- Watch for 429s and space out automated calls.
When it happens
Trigger: Calling twitter list-remove with a username whose UserByScreenName fetch returns a non-OK HTTP status (401/403 auth issues, 404 unknown user, 429 rate limit) or a payload without data.user.result.rest_id (suspended/deactivated account, protected/withheld user, or an API shape change after Twitter rotates the query ID).
Common situations: Typo in the handle; the account was renamed, suspended, or deleted; the ct0 cookie/session is stale so Twitter returns 401/403; hard rate-limiting (429); Twitter changed the UserByScreenName response schema; a hardcoded query ID went stale.
Related errors
- x.com
- Could not resolve user @${username}
- Could not fetch lists: ${listsData.__error}
- 12306 tk auth cookie missing
- ${probe.detail}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e5bc0e7da2b4d7aa.
Report an issue: GitHub.