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

  1. Log the actual r2.status to distinguish 401/403 (auth/CSRF) from 404 (bad userId) from 429 (rate limit)
  2. Re-login to refresh cookies and the csrftoken used to build X-CSRFToken
  3. Verify userId is a valid numeric Instagram user id (resolve the username first if needed)
  4. 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

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


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