jackwener/OpenCLI · error · CommandExecutionError
Twitter device-follow fetch failed: ${data.detail || 'unknow
Error message
Twitter device-follow fetch failed: ${data.detail || 'unknown error'} What it means
The device-follow fetch is executed inside the browser page via an injected async function; any exception thrown there (network failure, CORS, aborted request, undefined variable) is caught and returned as errorKind 'exception' with String(e.message || e), then rethrown on the Node side as a CommandExecutionError with the detail appended.
Source
Thrown at clis/twitter/device-follow.js:163
});
const data = await page.evaluate(`async () => {
try {
const r = await fetch("${apiUrl}", { method: "GET", headers: ${headers}, credentials: 'include' });
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;View on GitHub (pinned to 49907e53dc)
Solutions
- Retry the command — many failures are transient network issues
- Keep the browser page open and idle until the command completes
- Disable ad-blocker/privacy extensions for x.com or use a clean profile
- Check data.detail in the message for the underlying exception text
- Update the CLI if x.com changed its frontend/API surface
Defensive patterns
Strategy: retry
Validate before calling
// Pre-check: ensure the x.com page is loaded and stable before invoking
await page.waitForLoadState('domcontentloaded'); Try / catch
try {
await cli.twitter.deviceFollow();
} catch (e) {
if (e instanceof CommandExecutionError && /fetch failed/.test(e.message)) {
// transient in-page exception: back off and retry
await sleep(5000);
return retry(upTo = 3);
}
throw e;
} Prevention
- Keep the browser window open and idle while the command runs
- Ensure a stable network connection; avoid switching VPN/proxy mid-run
- Retry transient fetch failures with backoff
- Update the CLI if x.com frontend changes break the injected script
When it happens
Trigger: The in-page fetch to the device_follow GraphQL endpoint throws: network interruption, request blocked/aborted, x.com page navigated mid-fetch, or a bug in the injected script (e.g. referencing an undefined helper).
Common situations: Flaky network or proxy dropping the request; closing or navigating the browser window while the command runs; x.com frontend changes breaking injected code; extensions (ad blockers) interfering with fetch.
Related errors
- Claude whoami failed: ${result.detail}
- Jike identity probe failed: ${probe.detail}
- Midjourney browser-context media fetch failed: ${errorMessag
- subreddit-info failed: ${result.detail}
- exception
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/47cad7e0e7afe530.
Report an issue: GitHub.