{"record":{"id":"ea24d2ba5b5488fe","repo":"jackwener/OpenCLI","slug":"returned-malformed-items-payload","errorCode":null,"errorMessage":" returned malformed items payload","messagePattern":" returned malformed items payload","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"clis/instagram/save.js","lineNumber":35,"sourceCode":"    columns: ['status', 'user', 'post'],\n    pipeline: [\n        { navigate: 'https://www.instagram.com' },\n        { evaluate: `(async () => {\n  const username = \\${{ args.username | json }};\n  const idx = \\${{ args.index }} - 1;\n  if (!Number.isInteger(idx) || idx < 0) throw new Error('index must be a positive integer');\n  const headers = { 'X-IG-App-ID': '936619743392459' };\n  const opts = { credentials: 'include', headers };\n  async function readInstagramJson(response, label) {\n    try {\n      return await response.json();\n    } catch {\n      throw new Error(label + ' returned invalid JSON');\n    }\n  }\n  function getPostFromFeed(feed, label) {\n    if (!feed || typeof feed !== 'object' || !Array.isArray(feed.items)) {\n      throw new Error(label + ' returned malformed items payload');\n    }\n    if (idx >= feed.items.length) throw new Error('Post index ' + (idx + 1) + ' not found');\n    const post = feed.items[idx];\n    const pkRaw = post?.pk ?? post?.id;\n    const pk = typeof pkRaw === 'number' ? String(pkRaw) : (typeof pkRaw === 'string' ? pkRaw.trim() : '');\n    if (!/^\\\\d+$/.test(pk)) throw new Error(label + ' returned malformed post row');\n    const caption = typeof post?.caption?.text === 'string' ? post.caption.text.substring(0, 60) : '';\n    return { pk, caption };\n  }\n  function assertOkStatus(payload, label) {\n    if (!payload || typeof payload !== 'object' || payload.status !== 'ok') {\n      throw new Error(label + ' returned no success evidence');\n    }\n  }\n\n  // web_profile_info answers HTTP 400 for business accounts; feed-by-username needs no user id. See #2234.\n  const r1 = await fetch('https://www.instagram.com/api/v1/feed/user/' + encodeURIComponent(username) + '/username/?count=' + (idx + 1), opts);\n  if (!r1.ok) throw new Error(r1.status === 404 ? 'User not found: ' + username : 'HTTP ' + r1.status + ' - make sure you are logged in to Instagram');","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/instagram/save.js#L17-L53","documentation":"Thrown by `getPostFromFeed` when the parsed feed-by-username response is valid JSON but lacks the expected shape: no `feed`, not an object, or `feed.items` is not an array. The CLI relies on `items` being the post array; any other shape means Instagram's API response changed or a non-feed payload (e.g. an error object with status 200) was returned.","triggerScenarios":"GET /api/v1/feed/user/<username>/username/ returns HTTP 200 with JSON that is not a feed object: an `{\"message\": ...}` error envelope, a `{\"status\": \"fail\"}` object, `items: null`, or Instagram shipping a schema change that renames/moves the `items` array.","commonSituations":"Instagram silently degrades a request with a 200 + error JSON (e.g. login required payload); the target account is private/deleted so the payload has no items; Instagram rolls out an API schema change that breaks the hardcoded shape; a bot-detection payload replaces the feed data.","solutions":["Log the raw response body once to see what Instagram actually returned, then confirm whether it is an error envelope or schema change.","Refresh the Instagram login session in the CLI's browser profile — a 200 error envelope often means the session is stale.","Retry later if it is a bot-detection/limiting payload; avoid rapid repeated invocations.","Update the CLI (or the hardcoded `feed.items` parsing) if Instagram changed its API schema."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isFeedPayload(x) {\n  return x !== null && typeof x === 'object' && Array.isArray(x.items);\n}","tryCatchPattern":"try {\n  await run(['instagram', 'save', username, '--index', String(i)]);\n} catch (e) {\n  if (String(e.message).includes('returned malformed items payload')) {\n    // API returned an unexpected envelope: refresh session and retry once,\n    // otherwise flag for CLI/schema update\n    await refreshInstagramLogin();\n    return retryOnce();\n  }\n  throw e;\n}","preventionTips":["Keep the CLI updated — a hardcoded feed.items parse breaks on Instagram schema changes.","Refresh the session if the payload is an error envelope (200 + fail status).","Avoid targets likely to return degraded payloads (deleted/private accounts)."],"tags":["schema-validation","api-change","instagram"],"backgroundTag":"unexpected-response-schema","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}