jackwener/OpenCLI · error · Error
Instagram follow returned invalid JSON
Error message
Instagram follow returned invalid JSON
What it means
This CLI's follow command issues a POST to Instagram's private web API endpoint /api/v1/friendships/create/<userId>/ from within the browser session. After the fetch resolves with a 2xx response, it calls r2.json(); if the body cannot be parsed as JSON (HTML error page, empty body, CSP/blocked response), the parse throws and this error is raised instead. It means Instagram answered HTTP-ok but not with a JSON friendship payload.
Source
Thrown at clis/instagram/follow.js:38
{ 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/create/' + 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 follow: HTTP ' + r2.status);
let d2;
try {
d2 = await r2.json();
} catch {
throw new Error('Instagram follow returned invalid JSON');
}
if (!d2 || typeof d2 !== 'object' || d2.status !== 'ok' || !d2.friendship_status || typeof d2.friendship_status !== 'object') {
throw new Error('Instagram follow returned no success evidence');
}
const status = d2.friendship_status.following ? 'Following' : d2.friendship_status.outgoing_request ? 'Request sent' : '';
if (!status) throw new Error('Instagram follow returned no success evidence');
return [{ status, username }];
})()
` },
],
});
View on GitHub (pinned to 49907e53dc)
Solutions
- Re-authenticate in the browser (log into instagram.com) so the POST returns the real JSON friendship payload instead of an HTML page.
- Retry the follow command; transient anti-bot challenges often clear on a fresh navigation.
- Check network tab / response body for the actual content to confirm whether it's a login redirect or challenge page.
- If persistent, verify the account isn't action-blocked (temporary follow restrictions) which can cause non-JSON soft responses.
Example fix
// before (caller assumes JSON always present)
const d2 = await r2.json();
// after (mirror library's guard)
let d2;
try { d2 = await r2.json(); }
catch { throw new Error('Instagram follow returned invalid JSON'); } Defensive patterns
Strategy: try-catch
Validate before calling
// Nothing to validate pre-call (server-side body), but ensure an authenticated session exists:
const loggedIn = document.cookie.includes('ds_user_id');
if (!loggedIn) throw new Error('Not logged into Instagram; follow would return non-JSON'); Type guard
function isJsonObject(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }
// usage: if (!isJsonObject(d2)) { /* treat as invalid JSON payload */ } Try / catch
try {
const res = await followUser(username);
} catch (e) {
if (String(e.message).includes('invalid JSON')) {
// re-login / refresh session, then retry once
} else throw e;
} Prevention
- Always operate from a logged-in browser session with fresh cookies.
- Check r.ok AND content-type: application/json before calling r.json().
- Log the raw response text on parse failure to diagnose HTML vs empty bodies.
- Avoid running during known anti-bot challenge windows; throttle writes.
When it happens
Trigger: The POST to /api/v1/friendships/create/ returns 2xx with a non-JSON body: an HTML login/challenge interstitial, an empty 204-style body, a Cloudflare/anti-bot challenge page, or a response intercepted/modified by an extension.
Common situations: Session cookies expired so Instagram serves an HTML redirect page with 200; logged-out or soft-banned account; Instagram A/B serving different content; corporate proxy or extension rewriting responses; rate-limit soft responses rendered as HTML.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- 12306 queryByTrainNo returned non-JSON body
- 12306 ${endpoint} returned non-JSON body
- Barchart greeks request failed: ${data.message || 'unknown e
- Failed to load Booking.com search page: ${err?.message || er
- Failed to open Chess.com analysis board: ${error?.message ||
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/6def504ce022e401.
Report an issue: GitHub.