jackwener/OpenCLI · error · ArgumentError
twitter ${commandName} username must be a valid Twitter/X ha
Error message
twitter ${commandName} username must be a valid Twitter/X handle What it means
`resolveUserTimelineContext` normalizes the raw username argument with `normalizeTwitterScreenName`. If a non-empty raw value was supplied but normalization yields an empty string, it throws this ArgumentError naming the command (`tweets`, `collection`, etc.). The library requires a syntactically valid Twitter/X handle for every timeline-style command; it refuses to guess from a malformed input.
Source
Thrown at clis/twitter/user-timeline.js:162
if (cursor) vars.cursor = cursor;
return appendGraphqlParams(`/i/api/graphql/${normalized.queryId}/UserTweets`, vars, normalized);
}
export function buildUserByScreenNameUrl(operation, screenName) {
const normalized = normalizeUserByScreenNameOperation(operation);
const vars = { screen_name: screenName, withSafetyModeUserFields: true };
return appendGraphqlParams(`/i/api/graphql/${normalized.queryId}/UserByScreenName`, vars, normalized);
}
export async function resolveUserTimelineContext(
page,
rawUsername,
{ allowLoggedInDefault = false, commandName = 'tweets' } = {},
) {
const raw = String(rawUsername ?? '').trim();
let username = normalizeTwitterScreenName(raw);
if (raw && !username) {
throw new ArgumentError(
`twitter ${commandName} username must be a valid Twitter/X handle`,
commandName === 'collection'
? 'Example: opencli twitter collection @jack --until 2026-07-23T00:00:00Z'
: 'Example: opencli twitter tweets @jack --limit 20',
);
}
if (!username && !allowLoggedInDefault) {
throw new ArgumentError('twitter collection username must be a valid Twitter/X handle', 'Example: opencli twitter collection @jack --until 2026-07-23T00:00:00Z');
}
if (!username) {
await page.goto('https://x.com/home');
await page.wait({ selector: '[data-testid="primaryColumn"]' });
const href = unwrapBrowserResult(await page.evaluate(`() => {
const link = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
return link ? link.getAttribute('href') : null;
}`));
if (!href || typeof href !== 'string') {
throw new AuthRequiredError('x.com', 'Could not detect logged-in user. Are you logged in?');View on GitHub (pinned to 49907e53dc)
Solutions
- Pass a bare handle, with or without a leading @: `opencli twitter tweets @jack --limit 20`.
- Strip the URL down to the screen name if you copied a profile link (x.com/jack -> jack).
- Quote the argument in your shell if it contains special characters.
- Check for invisible characters/whitespace pasted from a document; retype the handle.
Example fix
// before opencli twitter tweets https://x.com/jack --limit 20 // after opencli twitter tweets @jack --limit 20
Defensive patterns
Strategy: validation
Validate before calling
const SCREEN_NAME_RE = /^[A-Za-z0-9_]{1,15}$/;
function toHandle(raw) {
const s = String(raw ?? '').trim().replace(/^@/, '');
if (!SCREEN_NAME_RE.test(s)) {
throw new Error(`'${raw}' is not a valid Twitter/X handle; pass a bare name like @jack`);
}
return s;
}
// use: opencli twitter tweets ${toHandle(input)} --limit 20 Type guard
function isValidHandle(raw) {
return /^[A-Za-z0-9_]{1,15}$/.test(String(raw ?? '').trim().replace(/^@/, ''));
} Try / catch
try {
await cli.run(['twitter', 'tweets', username, '--limit', '20']);
} catch (e) {
if (/must be a valid Twitter\/X handle/.test(e.message)) {
console.error('Extract the screen name from URLs first, e.g. x.com/jack -> jack');
} else throw e;
} Prevention
- Always pass a bare handle (optional leading @), never a profile URL.
- Quote arguments containing @ or special characters in your shell.
- Sanitize inputs extracted from URLs before passing them.
- Retype pasted handles to eliminate invisible characters.
When it happens
Trigger: Passing something like `https://x.com/jack`, `@`, `jack!`, a full URL, or whitespace-plus-symbols as the username argument to a twitter command routed through resolveUserTimelineContext with commandName not equal to 'collection'.
Common situations: Users pasting a profile URL where a handle is expected; stray characters or quotes around the handle; Windows shell mangling `@` or `%` characters; forgetting that only bare screen names (with optional leading @) are accepted.
Related errors
- twitter likes username must be a valid Twitter/X handle
- --resume-file requires --all
- twitter download requires either <username> or --tweet-url
- Use either <username> or --tweet-url, not both
- twitter download username must be a valid Twitter/X handle
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/5c1f78da722dacb9.
Report an issue: GitHub.