{"record":{"id":"56ab0ce05d766ed3","repo":"jackwener/OpenCLI","slug":"twitter-tweet-media-extraction-returned-malformed","errorCode":null,"errorMessage":"Twitter tweet media extraction returned malformed payload","messagePattern":"Twitter tweet media extraction returned malformed payload","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/download.js","lineNumber":450,"sourceCode":"          let src = img.src || '';\n          src = src.replace(/&name=\\\\w+$/, '&name=large');\n          if (!src.includes('&name=')) src = src + '&name=large';\n          out.push({ type: 'image', url: src });\n        });\n        document.querySelectorAll('video').forEach(video => {\n          const src = video.src || '';\n          if (src) out.push({ type: 'video', url: src });\n        });\n        document.querySelectorAll('[data-testid=\"videoPlayer\"]').forEach(player => {\n          const tweetLink = player.closest('article')?.querySelector('a[href*=\"/status/\"]');\n          const href = tweetLink?.getAttribute('href') || '';\n          if (href) out.push({ type: 'video-tweet', url: 'https://x.com' + href });\n        });\n        return out;\n      })()\n    `));\n    if (!Array.isArray(items)) {\n        throw new CommandExecutionError('Twitter tweet media extraction returned malformed payload');\n    }\n    if (items.length === 0) {\n        throw new EmptyResultError(`twitter download ${target.id}`, 'No media found in the tweet');\n    }\n    const cookies = await page.getCookies({ domain: 'x.com' });\n    const seen = new Set();\n    const unique = items.filter((m) => {\n        if (seen.has(m.url)) return false;\n        seen.add(m.url);\n        return true;\n    }).map((m) => {\n        return { ...m, tweet_id: target.id };\n    });\n    return downloadTwitterMedia(unique, {\n        output,\n        subdir: 'tweets',\n        cookies: formatCookieHeader(cookies),\n        browserCookies: cookies,","sourceCodeStart":432,"sourceCodeEnd":468,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/download.js#L432-L468","documentation":"downloadSingleTweet scrapes media by evaluating DOM queries in the page and unwrapping the result with unwrapBrowserResult. If the evaluated result is not an array (browser evaluation failed, returned an error wrapper, or was null), the command throws CommandExecutionError 'Twitter tweet media extraction returned malformed payload' rather than proceeding with bad data.","triggerScenarios":"page.evaluate failing or returning a non-array: the browser script threw and the wrapper captured an error object, the page didn't load a tweet (login wall, deleted tweet, rate-limit interstitial), or unwrapBrowserResult returned null for a failed evaluation.","commonSituations":"Tweet deleted or account protected so the media DOM never renders; x.com serving a login-required page so selectors match nothing but evaluation errors out; browser navigation timed out before the 3-second wait completed; x.com markup changes breaking the script.","solutions":["Verify the tweet URL loads in a logged-in browser and still contains media","Retry the command in case of a transient page-load failure","Update the CLI's extraction script if x.com changed its DOM structure","Check the tweet URL is well-formed (parseTweetUrl succeeded but the tweet 404'd)"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"const items = await extractMedia(page);\nif (!Array.isArray(items) || items.length === 0) {\n  throw new Error('Tweet page did not yield media; verify the URL and login state');\n}","typeGuard":"const isMediaItems = (v) => Array.isArray(v) && v.every((i) => i && typeof i.url === 'string' && typeof i.type === 'string');","tryCatchPattern":"try {\n  await cmd();\n} catch (err) {\n  if (err.message.includes('malformed payload')) {\n    // re-check the tweet URL in a browser, then retry once\n  }\n}","preventionTips":["Verify the tweet loads with media in a logged-in browser before scripting","Retry on transient page-load failures; the scrape waits only ~3 seconds","Keep the CLI updated when x.com DOM structure changes"],"tags":["dom-scraping","twitter","malformed-response"],"backgroundTag":"malured-dom-extraction-payload","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}