jackwener/OpenCLI · error · CommandExecutionError
Twitter tweet media extraction returned malformed payload
Error message
Twitter tweet media extraction returned malformed payload
What it means
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.
Source
Thrown at clis/twitter/download.js:450
let src = img.src || '';
src = src.replace(/&name=\\w+$/, '&name=large');
if (!src.includes('&name=')) src = src + '&name=large';
out.push({ type: 'image', url: src });
});
document.querySelectorAll('video').forEach(video => {
const src = video.src || '';
if (src) out.push({ type: 'video', url: src });
});
document.querySelectorAll('[data-testid="videoPlayer"]').forEach(player => {
const tweetLink = player.closest('article')?.querySelector('a[href*="/status/"]');
const href = tweetLink?.getAttribute('href') || '';
if (href) out.push({ type: 'video-tweet', url: 'https://x.com' + href });
});
return out;
})()
`));
if (!Array.isArray(items)) {
throw new CommandExecutionError('Twitter tweet media extraction returned malformed payload');
}
if (items.length === 0) {
throw new EmptyResultError(`twitter download ${target.id}`, 'No media found in the tweet');
}
const cookies = await page.getCookies({ domain: 'x.com' });
const seen = new Set();
const unique = items.filter((m) => {
if (seen.has(m.url)) return false;
seen.add(m.url);
return true;
}).map((m) => {
return { ...m, tweet_id: target.id };
});
return downloadTwitterMedia(unique, {
output,
subdir: 'tweets',
cookies: formatCookieHeader(cookies),
browserCookies: cookies,View on GitHub (pinned to 49907e53dc)
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)
Defensive patterns
Strategy: type-guard
Validate before calling
const items = await extractMedia(page);
if (!Array.isArray(items) || items.length === 0) {
throw new Error('Tweet page did not yield media; verify the URL and login state');
} Type guard
const isMediaItems = (v) => Array.isArray(v) && v.every((i) => i && typeof i.url === 'string' && typeof i.type === 'string');
Try / catch
try {
await cmd();
} catch (err) {
if (err.message.includes('malformed payload')) {
// re-check the tweet URL in a browser, then retry once
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to add @${username} to list ${listId}: invalid member
- 12306 tk auth cookie missing
- ${probe.detail}
- 12306 whoami failed: ${probe.detail}
- Unexpected 12306 probe: ${JSON.stringify(probe)}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/56ab0ce05d766ed3.
Report an issue: GitHub.