jackwener/OpenCLI · error
Failed to unfollow: HTTP ' + r2.status
Error message
Failed to unfollow: HTTP ' + r2.status
What it means
Thrown in clis/instagram/unfollow.js when the in-page POST to Instagram's friendships/destroy API returns a non-OK HTTP status. The request runs inside the browser context with session cookies and the CSRF token, so failures indicate Instagram rejected the unfollow (auth, rate limit, or bad user id).
Source
Thrown at clis/instagram/unfollow.js:33
},
],
columns: ['status', 'username'],
pipeline: [
{ navigate: 'https://www.instagram.com' },
{ evaluate: `(async () => {
const username = \${{ args.username | json }};
const headers = { 'X-IG-App-ID': '936619743392459' };
const opts = { credentials: 'include', headers };
${buildResolveInstagramUserIdJs()}
const csrf = document.cookie.match(/csrftoken=([^;]+)/)?.[1] || '';
const r2 = await fetch('https://www.instagram.com/api/v1/friendships/destroy/' + userId + '/', {
method: 'POST',
credentials: 'include',
headers: { ...headers, 'X-CSRFToken': csrf, 'Content-Type': 'application/x-www-form-urlencoded' },
});
if (!r2.ok) throw new Error('Failed to unfollow: HTTP ' + r2.status);
let d2;
try {
d2 = await r2.json();
} catch {
throw new Error('Instagram unfollow returned invalid JSON');
}
if (!d2 || typeof d2 !== 'object' || d2.status !== 'ok') {
throw new Error('Instagram unfollow returned no success evidence');
}
return [{ status: 'Unfollowed', username }];
})()
` },
],
});
View on GitHub (pinned to 49907e53dc)
Solutions
- Log the actual r2.status to distinguish 401/403 (auth/CSRF) from 404 (bad userId) from 429 (rate limit)
- Re-login to refresh cookies and the csrftoken used to build X-CSRFToken
- Verify userId is a valid numeric Instagram user id (resolve the username first if needed)
- Slow down: add delays between unfollows to avoid rate limiting
Example fix
// before
if (!r2.ok) throw new Error('Failed to unfollow: HTTP ' + r2.status);
// after
if (!r2.ok) {
if (r2.status === 429) throw new Error('Unfollow rate limited; wait before retrying');
throw new Error('Failed to unfollow: HTTP ' + r2.status + ' (check login and csrftoken)');
} Defensive patterns
Strategy: try-catch
Validate before calling
const csrf = document.cookie.match(/csrftoken=([^;]+)/)?.[1] || '';
if (!csrf) throw new Error('No csrftoken cookie; log in before unfollowing');
if (!/^\\d+$/.test(userId)) throw new Error(`Invalid numeric userId: ${userId}`); Type guard
function isOkResponse(r) { return !!r && typeof r.status === 'number' && r.status >= 200 && r.status < 300; } Try / catch
try {
await unfollow(username);
} catch (e) {
if (/HTTP 403/.test(e.message)) { await relogin(); /* refresh csrf */ }
else if (/HTTP 429/.test(e.message)) { await sleep(60000); }
else if (/HTTP 404/.test(e.message)) { console.error('Unknown userId'); }
else throw e;
} Prevention
- Ensure the csrftoken cookie exists before POSTing
- Throttle unfollows with delays to avoid 429s
- Resolve usernames to valid numeric user ids first
- Re-login when cookies age out; detect 401/403 early
When it happens
Trigger: r2.ok is false on `fetch('https://www.instagram.com/api/v1/friendships/destroy/' + userId + '/', { method: 'POST', credentials: 'include', headers: { ..., 'X-CSRFToken': csrf } })` — e.g. 403 (missing/invalid CSRF or blocked), 404 (userId not found), 429 (rate limited).
Common situations: csrftoken cookie missing so the regex yields '' and Instagram returns 403, unfollowing too many users quickly (429), stale userId, or an expired session.
Related errors
- Instagram private route could not derive CSRF token from bro
- returned no success evidence
- Failed to save: HTTP ' + r2.status
- HTTP ' + res.status + ' - make sure you are logged in to Ins
- HTTP ' + res.status + ' - make sure you are logged in to Ins
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/66842f43e5da4165.
Report an issue: GitHub.