{"record":{"id":"07a5b6d19e776051","repo":"jackwener/OpenCLI","slug":"failed-to-fetch-following-http-r2-status","errorCode":null,"errorMessage":"Failed to fetch following: HTTP ' + r2.status","messagePattern":"Failed to fetch following: HTTP ' \\+ r2\\.status","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clis/instagram/following.js","lineNumber":36,"sourceCode":"  const limit = \\${{ args.limit }};\n  if (!Number.isInteger(limit) || limit < 1) throw new Error('limit must be a positive integer');\n  const headers = { 'X-IG-App-ID': '936619743392459' };\n  const opts = { credentials: 'include', headers };\n\n  ${buildResolveInstagramUserIdJs()}\n\n  const PAGE_SIZE = 50;\n  const results = [];\n  const seen = new Set();\n  const seenCursors = new Set();\n  let maxId = undefined;\n  const baseUrl = 'https://www.instagram.com/api/v1/friendships/' + userId + '/following/';\n\n  while (results.length < limit) {\n    const params = new URLSearchParams({ count: String(PAGE_SIZE) });\n    if (maxId) params.set('max_id', maxId);\n    const r2 = await fetch(baseUrl + '?' + params.toString(), opts);\n    if (!r2.ok) throw new Error('Failed to fetch following: HTTP ' + r2.status);\n    const d2 = await r2.json();\n    if (!d2 || typeof d2 !== 'object' || !Array.isArray(d2.users)) {\n      throw new Error('Instagram following returned malformed users payload');\n    }\n    const users = d2.users;\n    const sizeBefore = results.length;\n    for (const u of users) {\n      if (!u || typeof u !== 'object') {\n        throw new Error('Instagram following returned malformed user row');\n      }\n      const pkRaw = u.pk ?? u.pk_id ?? u.id;\n      const pk = typeof pkRaw === 'number' ? String(pkRaw) : (typeof pkRaw === 'string' ? pkRaw.trim() : '');\n      const usernameValue = typeof u.username === 'string' ? u.username.trim() : '';\n      if (!/^\\\\d+$/.test(pk) || !usernameValue) {\n        throw new Error('Instagram following returned malformed user row');\n      }\n      if (!pk || seen.has(pk)) continue;\n      seen.add(pk);","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/instagram/following.js#L18-L54","documentation":"In the following command's pagination loop, each page is fetched from https://www.instagram.com/api/v1/friendships/<userId>/following/?count=50(&max_id=...). A non-ok HTTP status on any page throws this error with the status code. Because it's inside the while(results.length < limit) loop, the error can fire mid-pagination even if earlier pages succeeded.","triggerScenarios":"401/403 (expired cookies) on page 1 or after; 429 rate limit triggered by repeated page fetches; 404 from bad userId; 5xx from Instagram; anti-bot challenge blocking subsequent paginated requests.","commonSituations":"Large limits causing many rapid paginated requests (429 after a few pages); session expiring between pages; following list of a private/blocked account returning 403; Instagram throttling max_id cursor requests.","solutions":["Re-authenticate in the browser session (401/403).","Lower --limit to fetch fewer pages and add delays between requests to avoid 429.","Retry with exponential backoff; capture the HTTP status from the message to decide.","Verify the username resolves to a valid, accessible userId (404/403)."],"exampleFix":"// before\ncatch (e) { console.error(e.message); }\n// after\ncatch (e) {\n  const m = /HTTP (\\d+)/.exec(e.message);\n  if (m && +m[1] === 429) { await sleep(60000); return retryWithLowerLimit(); }\n  if (m && (+m[1] === 401 || +m[1] === 403)) return promptRelogin();\n  throw e;\n}","handlingStrategy":"retry","validationCode":"// Pre-flight before paginating:\nconst authed = document.cookie.includes('ds_user_id');\nconst limitOk = Number.isInteger(limit) && limit >= 1;\nif (!authed || !limitOk) throw new Error('Preconditions failed: session or limit invalid');","typeGuard":null,"tryCatchPattern":"async function fetchFollowingPages(userId, limit) {\n  let maxId = null;\n  for (let attempt = 0; attempt < 3; attempt++) {\n    try {\n      const page = await fetchPage(userId, maxId);\n      maxId = page.next_max_id;\n      if (!maxId) break;\n    } catch (e) {\n      const code = +(/HTTP (\\d+)/.exec(String(e.message))?.[1] ?? 0);\n      if (code !== 429 && code < 500) throw e;\n      await new Promise(r => setTimeout(r, 2 ** attempt * 5000));\n    }\n  }\n}","preventionTips":["Add delays between paginated requests; large limits on big accounts trigger 429 quickly.","Keep the session alive across the whole pagination run (re-login on 401).","Parse the HTTP status out of the error message to choose retry vs abort.","Cap limit to what you need — each extra page is another rate-limited request."],"tags":["instagram","network","pagination","http-status","rate-limit"],"backgroundTag":"http-error-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}