jackwener/OpenCLI · error · AuthRequiredError

Could not find logged-in user profile link. Are you logged…

Error message

Could not find logged-in user profile link. Are you logged in?

What it means

When no user argument is supplied the command resolves the logged-in account by reading the href of the AppTabBar_Profile_Link element on x.com/home. If the selector returns null or a non-string (e.g. X renamed the testid, or the DOM differs), it cannot determine whose followers to fetch and throws AuthRequiredError. The second message variant means the page is likely not a logged-in home view.

Solutions

  1. Log into x.com in the tool's browser profile and confirm the profile avatar appears on home
  2. Pass the target handle explicitly (opencli twitter followers @handle) to bypass profile-link detection
  3. Update shared.js/library if X changed the data-testid, and re-run
  4. Retry after the page fully loads; transient hydration timing can hide the link

Example fix

// before (relies on logged-in home)
opencli twitter followers
// after (explicit target)
opencli twitter followers @elonmusk --limit 100
Defensive patterns

Strategy: try-catch

Validate before calling

const href = await page.evaluate(`() => document.querySelector('a[data-testid="AppTabBar_Profile_Link"]')?.getAttribute('href') ?? null`);
if (!href) throw new Error('Pass an explicit user: opencli twitter followers @handle');

Type guard

null

Try / catch

try {
  const rows = await opencli.twitter.followers();
} catch (err) {
  if (err instanceof AuthRequiredError && err.message.includes('profile link')) {
    const rows = await opencli.twitter.followers(myKnownHandle); // explicit-user fallback
  } else throw err;
}

Prevention

When it happens

Trigger: Running without a user argument while the profile link a[data-testid="AppTabBar_Profile_Link"] is missing from the rendered home page — logged-out state, X A/B test changing the testid, page not fully hydrated, or a localized/variant DOM.

Common situations: X front-end update renaming the data-testid; scraping before hydration completes; semi-logged-in state where ct0 exists but home renders logged-out chrome; running against an unsupported locale/layout experiment.

Related errors


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

Appendix: source

Thrown at clis/twitter/followers.js:120

            throw new ArgumentError('twitter followers user must be a valid Twitter/X handle', 'Example: opencli twitter followers @elonmusk --limit 100');
        }
        await page.goto('https://x.com/home');
        await page.wait({ selector: '[data-testid="primaryColumn"]' });
        const cookies = await page.getCookies({ url: 'https://x.com' });
        const ct0 = cookies.find((cookie) => cookie.name === 'ct0')?.value || null;
        if (!ct0) {
            throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');
        }

        if (!targetUser) {
            // Bridge wraps primitive page.evaluate returns as { session, data:<value> };
            // unwrap so the href string is usable downstream.
            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 find logged-in user profile link. Are you logged in?');
            }
            targetUser = normalizeScreenName(href);
            if (!targetUser) {
                throw new AuthRequiredError('x.com', 'Could not find logged-in user profile link. Are you logged in?');
            }
        }
        if (!targetUser) {
            throw new ArgumentError('twitter followers user cannot be empty', 'Example: opencli twitter followers @elonmusk --limit 100');
        }

        await page.installInterceptor('/Followers?');
        const targetPath = `/${targetUser}/followers`;
        await page.evaluate(`() => {
            const targetPath = ${JSON.stringify(targetPath)};
            window.history.pushState({}, '', targetPath);
            window.dispatchEvent(new PopStateEvent('popstate', { state: {} }));
        }`);
        try {

View on GitHub (pinned to 49907e53dc)