jackwener/OpenCLI · error · AuthRequiredError

Twitter device-follow returned HTTP ${data.error}

Error message

Twitter device-follow returned HTTP ${data.error}

What it means

When the GraphQL response parses but carries an error status of 401 or 403, the command throws AuthRequiredError for x.com with the HTTP code embedded. This means the session's credentials were rejected by the API rather than the response being malformed.

Source

Thrown at clis/twitter/device-follow.js:167

          if (!r.ok) return { error: r.status };
          try {
            return await r.json();
          } catch (e) {
            return { errorKind: 'non_json', detail: String(e && e.message || e) };
          }
        } catch (e) {
          return { errorKind: 'exception', detail: String(e && e.message || e) };
        }
      }`);
        if (data?.errorKind === 'non_json') {
            throw new CommandExecutionError(`Twitter device-follow returned non-JSON response: ${data.detail || 'unknown parse error'}`);
        }
        if (data?.errorKind === 'exception') {
            throw new CommandExecutionError(`Twitter device-follow fetch failed: ${data.detail || 'unknown error'}`);
        }
        if (data?.error) {
            if (data.error === 401 || data.error === 403) {
                throw new AuthRequiredError('x.com', `Twitter device-follow returned HTTP ${data.error}`);
            }
            throw new CommandExecutionError(describeTwitterApiError('device_follow', data.error));
        }
        const parsed = parseDeviceFollow(data, new Set());
        if (!parsed) {
            throw new CommandExecutionError('Twitter device-follow response was missing the expected timeline/globalObjects shape.');
        }
        if (parsed.malformedEntries > 0 || parsed.unmatchedTweetEntries > 0) {
            throw new CommandExecutionError('Twitter device-follow entries could not be joined to tweet/user objects.');
        }
        if (parsed.rows.length === 0) {
            throw new EmptyResultError('twitter device-follow', 'No device-follow notification tweets found.');
        }
        const rows = parsed.rows;
        const trimmed = rows.slice(0, limit);
        return applyTopByEngagement(trimmed, kwargs['top-by-engagement']);
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log out and back into x.com to refresh the session and ct0 cookie
  2. Clear x.com cookies in the CLI-managed profile and re-authenticate
  3. Verify the whole cookie jar comes from the same session (don't mix ct0 from one profile with another session)
  4. If 403 persists on a clean session, the account/IP may be flagged — wait or switch networks

Example fix

// before (stale cookies)
cli twitter device-follow  # AuthRequiredError: HTTP 401
// after
# clear x.com cookies in the managed profile, log in again, then
cli twitter device-follow
Defensive patterns

Strategy: fallback

Validate before calling

const cookies = await page.getCookies({ url: 'https://x.com' });
const ok = cookies.some((c) => c.name === 'ct0' && c.value);
// also ensure cookies all come from one session/profile

Try / catch

try {
  await cli.twitter.deviceFollow();
} catch (e) {
  if (e instanceof AuthRequiredError && /HTTP 40[13]/.test(e.message)) {
    console.error('Session rejected — clear x.com cookies and log in again.');
    return await reauthenticateThen(fn);
  }
  throw e;
}

Prevention

When it happens

Trigger: The device_follow API responds with HTTP 401 (expired/invalid auth token or csrf mismatch) or 403 (forbidden — suspended account, bot detection, or token/cookie pair out of sync).

Common situations: Stale bearer token after x.com rotated keys; ct0 cookie not matching the session (copied cookies from another browser); account flagged or suspended; IP flagged by Twitter's anti-bot system.

Related errors


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