jackwener/OpenCLI · warning · TimeoutError
twitter follow confirmation
Error message
twitter follow confirmation
What it means
After running the in-page follow flow, the command checks result.unconfirmed — set when the follow button's write had started but the confirmation (button-state change / toast) was never observed within the 1.5s window because the evaluate caught an exception. It throws TimeoutError('twitter follow confirmation', 1.5) with a hint that the follow may already have succeeded, so users check the profile before retrying to avoid double-follow side effects.
Source
Thrown at clis/twitter/follow.js:63
}
writeStarted = true;
followBtn.click();
await new Promise(r => setTimeout(r, 1500));
// Verify
const verify = document.querySelector('[data-testid$="-unfollow"]');
if (verify) {
return { ok: true, message: 'Successfully followed @${username}.' };
} else {
return { ok: false, unconfirmed: true, message: 'Follow action initiated but UI did not update.' };
}
} catch (e) {
return { ok: false, unconfirmed: writeStarted, message: e.toString() };
}
})()`);
if (result.unconfirmed) {
throw new TimeoutError('twitter follow confirmation', 1.5, `${result.message} Check the profile before retrying; the follow may already have succeeded.`);
}
if (!result.ok) {
throw new CommandExecutionError(result.message, 'Nothing changed. Open the profile in the browser and retry.');
}
await page.wait(2);
return [{
status: 'success',
message: result.message
}];
}
});
View on GitHub (pinned to 49907e53dc)
Solutions
- Open https://x.com/<username> in the browser and check whether you already follow the account before retrying — the write likely succeeded.
- Simply retry the command; if already following, the follow flow will report the existing state rather than re-following.
- Check for X UI changes or interstitials (rate-limit modal, login prompt) in the browser that block the confirmation signal.
- Retry when the network is faster; persistent unconfirmed timeouts on a stable connection suggest a stale extractor (update/report).
Example fix
// before // timeout on slow network -> assume failure, follow again // after // 1) verify at https://x.com/<username> whether the follow landed // 2) only retry if not already following
Defensive patterns
Strategy: retry
Validate before calling
// No pre-call check can fully prevent this; reduce likelihood by ensuring a
// fast, stable connection and no X interstitials before invoking:
async function pageLooksClean(page) {
await page.wait({ selector: '[data-testid="primaryColumn"]' }); // page fully loaded
return true;
} Type guard
function isConfirmedFollowResult(r) {
return typeof r === 'object' && r !== null && r.ok === true && !r.unconfirmed;
} Try / catch
try {
await follow(username);
} catch (e) {
if (e.code === 'TIMEOUT' && e.message.includes('twitter follow confirmation')) {
// The write may have landed — verify before retrying to avoid confusion
const following = await checkFollowingOnProfile(username); // manual or scripted
if (!following) return retryWithBackoff(follow, username);
return; // already followed
}
throw e;
} Prevention
- After an unconfirmed timeout, always check the profile before re-running — the follow may have succeeded.
- Run on a stable connection; confirmation is observed within a short 1.5s window.
- Watch for X UI/AB-test changes that move the confirmation indicator; keep the CLI updated.
- Handle protected accounts: pending follow requests may never confirm like normal follows.
When it happens
Trigger: The in-page follow script threw after writeStarted = true (e.g. the confirmation selector/toast never appeared, X's UI updated, or the page navigated); slow network or X latency means confirmation takes longer than the 1.5s window; a modal or rate-limit interstitial blocked the confirmation signal.
Common situations: Slow connections where the button state flip takes >1.5s; X A/B UI changes moving the confirmation indicator; the target account is protected/private so the follow enters a pending state that the script does not recognize; rate limiting altering the response.
Related errors
- Unexpected 12306 probe: ${JSON.stringify(probe)}
- Temporary chat did not become visible after selecting the me
- grok export-all history dialog
- Instagram caption editor did not appear
- Nothing changed. Open the profile in the browser and retry.
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/c1de709f72c1df97.
Report an issue: GitHub.