jackwener/OpenCLI · error

returned malformed post row

Error message

 returned malformed post row

What it means

Thrown by `getPostFromFeed` when the selected feed item has no usable post identifier: `post.pk ?? post.id` is missing, or its string form does not match /^\d+$/. The media `pk` is required to build the save URL (/api/v1/web/save/<pk>/save/), so a row without a numeric pk cannot be saved.

Source

Thrown at clis/instagram/save.js:41

  if (!Number.isInteger(idx) || idx < 0) throw new Error('index must be a positive integer');
  const headers = { 'X-IG-App-ID': '936619743392459' };
  const opts = { credentials: 'include', headers };
  async function readInstagramJson(response, label) {
    try {
      return await response.json();
    } catch {
      throw new Error(label + ' returned invalid JSON');
    }
  }
  function getPostFromFeed(feed, label) {
    if (!feed || typeof feed !== 'object' || !Array.isArray(feed.items)) {
      throw new Error(label + ' returned malformed items payload');
    }
    if (idx >= feed.items.length) throw new Error('Post index ' + (idx + 1) + ' not found');
    const post = feed.items[idx];
    const pkRaw = post?.pk ?? post?.id;
    const pk = typeof pkRaw === 'number' ? String(pkRaw) : (typeof pkRaw === 'string' ? pkRaw.trim() : '');
    if (!/^\\d+$/.test(pk)) throw new Error(label + ' returned malformed post row');
    const caption = typeof post?.caption?.text === 'string' ? post.caption.text.substring(0, 60) : '';
    return { pk, caption };
  }
  function assertOkStatus(payload, label) {
    if (!payload || typeof payload !== 'object' || payload.status !== 'ok') {
      throw new Error(label + ' returned no success evidence');
    }
  }

  // web_profile_info answers HTTP 400 for business accounts; feed-by-username needs no user id. See #2234.
  const r1 = await fetch('https://www.instagram.com/api/v1/feed/user/' + encodeURIComponent(username) + '/username/?count=' + (idx + 1), opts);
  if (!r1.ok) throw new Error(r1.status === 404 ? 'User not found: ' + username : 'HTTP ' + r1.status + ' - make sure you are logged in to Instagram');
  const { pk, caption } = getPostFromFeed(await readInstagramJson(r1, 'Instagram feed-by-username'), 'Instagram feed-by-username');

  const csrf = document.cookie.match(/csrftoken=([^;]+)/)?.[1] || '';
  const r2 = await fetch('https://www.instagram.com/api/v1/web/save/' + pk + '/save/', {
    method: 'POST', credentials: 'include',
    headers: { ...headers, 'X-CSRFToken': csrf, 'Content-Type': 'application/x-www-form-urlencoded' },

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Try a different --index value — the row at that position may be an ad or non-media entry without a pk.
  2. Dump the item's JSON (or check in DevTools on instagram.com) to find where the numeric pk actually sits in the current schema.
  3. Update the CLI's pk extraction if Instagram changed the field layout (e.g. nested item.media.pk).
  4. Save the post manually on the web if the feed keeps returning rows without identifiers.
Defensive patterns

Strategy: type-guard

Type guard

function hasNumericPk(post) {
  const raw = post?.pk ?? post?.id;
  return (typeof raw === 'number' && Number.isFinite(raw)) ||
         (typeof raw === 'string' && /^\d+$/.test(raw.trim()));
}

Try / catch

try {
  await run(['instagram', 'save', username, '--index', String(i)]);
} catch (e) {
  if (String(e.message).includes('returned malformed post row')) {
    // feed row has no usable pk (ad/placeholder) — try the next index
    return run(['instagram', 'save', username, '--index', String(i + 1)]);
  }
  throw e;
}

Prevention

When it happens

Trigger: feed.items[idx] is a non-media object (ad, banner, story tray entry, or `clip`/`awaiting` wrapper) lacking `pk`/`id`, or the id is a non-numeric string, so the pk regex test fails.

Common situations: Instagram injects ads or suggested-post entries into user feeds; carousel/story placeholder rows appear in items; the target post is a reel/clip wrapped in an envelope where pk lives at a nested path; an Instagram schema change renames the id field.

Understand the failure class

Related errors


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