jackwener/OpenCLI · error · CommandExecutionError
Twitter UserMedia returned malformed timeline instructions
Error message
Twitter UserMedia returned malformed timeline instructions
What it means
After validating the user result, requireUserMediaPayload expects a timeline instructions array at result.timeline_v2.timeline.instructions or the legacy result.timeline.timeline.instructions path. If neither is an array, it throws 'malformed timeline instructions'. This protects the recursive parser (parseUserMedia/visit) from walking a structure with no entries to visit.
Source
Thrown at clis/twitter/download.js:249
}
if (result.ok === false) {
throwGraphqlFetchError(context, Number(result.status) || 0, typeof result.error === 'string' ? result.error : '');
}
throw new CommandExecutionError(`Twitter ${context} returned malformed fetch result`);
}
function requireUserMediaPayload(data) {
const payload = requireObjectPayload(data, 'UserMedia');
if (Array.isArray(payload.errors) && payload.errors.length > 0) {
throw new CommandExecutionError(`Twitter UserMedia returned GraphQL errors: ${JSON.stringify(payload.errors).slice(0, 200)}`);
}
const result = payload.data?.user?.result;
if (!result || typeof result !== 'object') {
throw new CommandExecutionError('Twitter UserMedia returned malformed user result');
}
const instructions = result.timeline_v2?.timeline?.instructions || result.timeline?.timeline?.instructions;
if (!Array.isArray(instructions)) {
throw new CommandExecutionError('Twitter UserMedia returned malformed timeline instructions');
}
return payload;
}
function parseUserMedia(data, seen) {
const items = [];
let nextCursor = null;
const result = requireUserMediaPayload(data).data.user.result;
const instructionSets = [
result.timeline_v2?.timeline?.instructions,
result.timeline?.timeline?.instructions,
].filter(Array.isArray);
const instructions = instructionSets.flat();
const visit = (value) => {
if (!value || typeof value !== 'object') return;
if (value.type === 'TimelinePinEntry') return;
if (value.tweet_results?.result) {
const raw = value.tweet_results.result;View on GitHub (pinned to 49907e53dc)
Solutions
- Check the latest x.com UserMedia GraphQL response in browser devtools and update the instructions lookup path (timeline_v2 vs timeline) in requireUserMediaPayload.
- Re-run after confirming the account actually has media; a truly empty timeline may need an explicit empty-result branch instead of an error.
- Verify the queryId matches the current x.com build — mismatched query ids can yield truncated/alternate payloads.
- Add support for any new container field X introduced (e.g. additional result.* timeline variants).
Example fix
// before
const instructions = result.timeline_v2?.timeline?.instructions || result.timeline?.timeline?.instructions;
// after: tolerate additional/new container paths
const instructions = result.timeline_v2?.timeline?.instructions
|| result.timeline?.timeline?.instructions
|| result.timeline_response?.timeline?.instructions; Defensive patterns
Strategy: validation
Validate before calling
// Probe the endpoint shape before full parsing
const instructions = result?.timeline_v2?.timeline?.instructions ?? result?.timeline?.timeline?.instructions;
if (!Array.isArray(instructions)) {
console.warn('UserMedia timeline shape unrecognized — update parser');
} Type guard
function hasTimelineInstructions(result) {
return Array.isArray(result?.timeline_v2?.timeline?.instructions)
|| Array.isArray(result?.timeline?.timeline?.instructions);
} Try / catch
try {
await twitterDownload(username);
} catch (err) {
if (err instanceof CommandExecutionError && err.message.includes('malformed timeline instructions')) {
// pin/upgrade the parser to the current X GraphQL schema, then retry
} else throw err;
} Prevention
- Track X GraphQL UserMedia schema changes (timeline_v2 vs timeline paths) regularly
- Capture raw responses in devtools to diff new container fields
- Pin/verify the GraphQL queryId against the current x.com build
- Add an explicit empty-timeline branch so accounts with no media don't error
When it happens
Trigger: The user result object exists but contains no timeline_v2/timeline instructions — e.g. the account has no media timeline payload, X changed the response schema (timeline_v2 renamed/moved), or the entry has an empty/unexpected node shape.
Common situations: X shipping a GraphQL schema change to UserMedia (very common with timeline_v2 vs timeline migrations); new account with no tweets/media returning a different node; A/B-tested response variants for some sessions.
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
- Twitter UserMedia returned malformed user result
- Twitter UserMedia returned GraphQL errors: ${JSON.stringify(
- Malformed Twitter follower: missing screen_name
- CreateList returned no list payload. Body: ${String(result.b
- CreateList returned a list payload without a numeric list id
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/81b9902087bd5dc0.
Report an issue: GitHub.