jackwener/OpenCLI · error · Error
STATE_VERIFY_FAIL
STATE_VERIFY_FAIL
Error message
STATE_VERIFY_FAIL: relation did not flip back to Follow within 5s; unfollow may not have been recorded
What it means
After clicking unfollow (and confirming), the script waits up to 5s for the button to flip from Following back to Follow. If the flip never happens it throws with code STATE_VERIFY_FAIL, meaning the unfollow action likely was not recorded by TikTok. This is a post-action state verification, not a selector problem.
Source
Thrown at clis/tiktok/unfollow.js:77
target.click();
// TikTok shows a confirm-unfollow dialog. Wait for it to render.
const dialogShown = await waitFor(() => buttonExists(CONFIRM_LABELS), { timeoutMs: 3000 });
if (dialogShown) {
const confirmBtn = findButtonByText(CONFIRM_LABELS);
if (!confirmBtn) {
throw new Error('BUTTON_NOT_FOUND: confirm-unfollow dialog detected but confirm button could not be located');
}
confirmBtn.click();
}
const flipped = await waitFor(
() => buttonExists(FOLLOW_LABELS) && !buttonExists(RELATION_LABELS),
{ timeoutMs: 5000 },
);
if (!flipped) {
ensureNoRateLimitOrThrow();
throw new Error('STATE_VERIFY_FAIL: relation did not flip back to Follow within 5s; unfollow may not have been recorded');
}
ensureNoRateLimitOrThrow();
return [{
username,
url: ${JSON.stringify(TIKTOK_HOST)} + '/@' + encodeURIComponent(username),
result: 'unfollowed',
}];
})()
`;
}
async function unfollowUser(page, args) {
const username = normalizeUsername(args.username);
const throwFailure = (error) => throwButtonWalkerError(error, {
authMessage: 'TikTok requires login to unfollow users',
failureMessage: `Failed to unfollow @${username}`,View on GitHub (pinned to 49907e53dc)
Solutions
- Re-run the unfollow for that user after a delay, then verify the relation state before continuing
- Slow down bulk unfollows (add delays between actions) to avoid silent rate-limit rejections
- Extend the 5s waitFor timeout if network latency is the cause
- Check ensureNoRateLimitOrThrow / any rate-limit notices on the page and back off when signaled
Example fix
// before
await unfollowUser(username);
// after
try {
await unfollowUser(username);
} catch (e) {
if (e.message.includes('STATE_VERIFY_FAIL')) {
await sleep(60000); // back off, then verify and retry
await unfollowUser(username);
} else throw e;
} Defensive patterns
Strategy: retry
Validate before calling
const before = await getRelationState(username); // 'Follow' | 'Following'
if (before !== 'Following') throw new Error(`${username} is not followed; nothing to unfollow`); Type guard
function relationIsFollowed(state) {
return state === 'Following' || state === 'Friends';
} Try / catch
async function unfollowSafe(username, attempts = 2) {
for (let i = 0; i < attempts; i++) {
try {
return await unfollowUser(username);
} catch (e) {
if (!e.message.includes('STATE_VERIFY_FAIL') || i === attempts - 1) throw e;
await new Promise(r => setTimeout(r, 30000)); // back off before retry
}
}
} Prevention
- Verify pre-state (Following) before attempting unfollow
- Add generous delays between bulk unfollow actions to avoid silent throttling
- After STATE_VERIFY_FAIL, independently re-check the relation before retrying
- Watch ensureNoRateLimitOrThrow signals and pause the whole batch when rate limits appear
When it happens
Trigger: The confirm dialog was accepted but the relation button never showed a Follow label within 5s: the click didn't register, TikTok silently rejected the action (rate limit/bot detection), the network request behind the unfollow failed, or the page was stale.
Common situations: Mass-unfollowing triggering server-side throttling that silently drops the action; slow network exceeding the 5s window; session expired so the mutation is rejected client-side; TikTok UI changing the post-action button state text.
Related errors
- STATE_VERIFY_FAIL: follow button did not flip to Following w
- Chess.com API returned HTTP ${resp.status} for ${url}
- coingecko derivatives returned HTTP 429 (rate limited)
- coingecko returned HTTP 429 (rate limited)
- ${label} returned HTTP 429 (rate limited)
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/abcb1c62b588accf.
Report an issue: GitHub.