jackwener/OpenCLI · error · CommandExecutionError
Malformed Twitter following user: missing screen_name
Error message
Malformed Twitter following user: missing screen_name
What it means
CommandExecutionError 'Malformed Twitter following user: missing screen_name' thrown by extractUser in clis/twitter/following.js. When a user_results.result object passes the __typename === 'User' check, its core.screen_name / legacy.screen_name are both empty, meaning X returned a user entity stripped of its handle. The library treats this as corrupt upstream data and fails loudly rather than emitting a row with an empty screen_name.
Source
Thrown at clis/twitter/following.js:74
withBirdwatchNotes: false,
withVoice: true,
withV2Timeline: true,
};
if (cursor)
vars.cursor = cursor;
return `/i/api/graphql/${queryId}/Following`
+ `?variables=${encodeURIComponent(JSON.stringify(vars))}`
+ `&features=${encodeURIComponent(JSON.stringify(FEATURES))}`;
}
function extractUser(result) {
if (!result || result.__typename !== 'User')
return null;
const core = result.core || {};
const legacy = result.legacy || {};
const screenName = core.screen_name || legacy.screen_name || '';
if (!screenName) {
throw new CommandExecutionError('Malformed Twitter following user: missing screen_name');
}
return {
screen_name: screenName,
name: core.name || legacy.name || '',
bio: legacy.description || result.profile_bio?.description || '',
followers: result.relationship_counts?.followers
?? legacy.followers_count
?? legacy.normal_followers_count
?? 0,
};
}
function parseFollowing(data) {
const users = [];
let nextCursor = null;
const instructions = data?.data?.user?.result?.timeline_v2?.timeline?.instructions
|| data?.data?.user?.result?.timeline?.timeline?.instructions
|| [];View on GitHub (pinned to 49907e53dc)
Solutions
- Retry the command — ghost/degraded user entities are often transient.
- Refresh the x.com session (cookies) so X serves complete user payloads.
- If persistent, check whether X changed the User payload shape (core/legacy) and update extractUser to read the new location.
- Skip past the offending entry externally by lowering --limit to exclude the malformed tail rows, then report the payload shape change.
Example fix
// before const screenName = core.screen_name || legacy.screen_name || ''; // after const screenName = core.screen_name || legacy.screen_name || result.core?.user_legacy?.screen_name || ''; // adapt to new X payload envelope
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate the payload shape you feed to extractUser before it throws
function hasScreenName(result) {
return Boolean(result && result.__typename === 'User' &&
((result.core && result.core.screen_name) || (result.legacy && result.legacy.screen_name)));
} Type guard
function isWellFormedUser(r) {
return Boolean(r && r.__typename === 'User' &&
typeof (r.core?.screen_name ?? r.legacy?.screen_name) === 'string' &&
(r.core?.screen_name ?? r.legacy?.screen_name) !== '');
} Try / catch
import { CommandExecutionError } from '@jackwener/opencli/errors';
try {
rows = await opencli.twitter.following(user, { limit });
} catch (e) {
if (e instanceof CommandExecutionError && /missing screen_name/.test(e.message)) {
return await opencli.twitter.following(user, { limit }); // transient ghost entity: retry once
}
throw e;
} Prevention
- Filter user_results.result entries through a screen_name presence guard in custom parsing
- Retry once on malformed-user errors before failing the pipeline
- Track X GraphQL payload-shape changes (core/legacy envelope) proactively
- Refresh sessions to avoid degraded/redacted user payloads
When it happens
Trigger: Parsing a Following GraphQL timeline where an entryId user- entry's itemContent.user_results.result has __typename 'User' but neither core.screen_name nor legacy.screen_name is populated — called from the user/zero command paths.
Common situations: X mid-flight API redesign moving screen_name to a new envelope location; suspended/ghost user entities left in a following list; partially redacted payloads served to lower-trust sessions; A/B payload variants where core is renamed (e.g. new naming while legacy is dropped).
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Bilibili creator comparison returned a malformed manuscript
- returned malformed items payload
- returned malformed post row
- LinkedIn connection miniProfile field ${field} was malformed
- LinkedIn connections API returned a malformed payload: missi
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/9f50b9f127ea0d09.
Report an issue: GitHub.