jackwener/OpenCLI · error · CommandExecutionError
Malformed Twitter follower: missing screen_name
Error message
Malformed Twitter follower: missing screen_name
What it means
This error is thrown by extractFollower when a follower entry parsed from Twitter's Followers GraphQL timeline has __typename 'User' but no screen_name in either the core or legacy blocks. The library treats a User entry without a handle as malformed because the screen_name column is the primary key of the output contract (columns: ['screen_name','name','bio']); it cannot emit a dedupable row without it. It surfaces as a CommandExecutionError rather than silently skipping the entry.
Source
Thrown at clis/twitter/followers.js:15
import { ArgumentError, AuthRequiredError, EmptyResultError, CommandExecutionError, TimeoutError } from '@jackwener/opencli/errors';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { looksLikePrivateTwitterTimeline, normalizeTwitterGraphqlPayload, normalizeTwitterScreenName, unwrapBrowserResult } from './shared.js';
const MAX_PAGINATION_PAGES = 100;
const CAPTURE_TIMEOUT_SECONDS = 10;
function extractFollower(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 follower: missing screen_name');
}
return {
screen_name: screenName,
name: core.name || legacy.name || '',
bio: result.profile_bio?.description || legacy.description || '',
};
}
function parseFollowers(value) {
const data = normalizeTwitterGraphqlPayload(value);
const users = [];
let nextCursor = null;
let bottomTerminated = false;
const instructions = data?.data?.user?.result?.timeline_v2?.timeline?.instructions
|| data?.data?.user?.result?.timeline?.timeline?.instructions
|| [];
for (const instruction of instructions) {
if (instruction?.type === 'TimelineTerminateTimeline' && instruction.direction === 'Bottom') {View on GitHub (pinned to 49907e53dc)
Solutions
- Update the library / shared.js payload normalization to the current Twitter GraphQL shape so core.screen_name or legacy.screen_name is found
- Re-run the command — transient malformed entries often disappear on retry
- Check whether the target account is suspended/restricted and try a different target user
- File a bug with the raw captured response so the parser can be extended
Example fix
// before
const screenName = core.screen_name || legacy.screen_name || '';
if (!screenName) {
throw new CommandExecutionError('Malformed Twitter follower: missing screen_name');
}
// after (skip instead of failing the whole run)
const screenName = core.screen_name || legacy.screen_name || result.screen_name || '';
if (!screenName) return null; Defensive patterns
Strategy: try-catch
Validate before calling
const result = entry?.itemContent?.user_results?.result;
if (result?.__typename === 'User' && !(result.core?.screen_name || result.legacy?.screen_name)) {
console.warn('skipping follower without screen_name');
} Type guard
function hasScreenName(r) {
return !!r && r.__typename === 'User' &&
typeof (r.core?.screen_name || r.legacy?.screen_name) === 'string' &&
(r.core?.screen_name || r.legacy?.screen_name).length > 0;
} Try / catch
try {
const rows = await opencli.twitter.followers(user, { limit });
} catch (err) {
if (err.message.includes('Malformed Twitter follower')) {
// schema drift: retry, update library, or fall back to partial data
} else throw err;
} Prevention
- Keep the library updated against current X GraphQL payloads
- Prefer skipping malformed entries over failing whole runs when tolerable
- Test parsers against recorded real API responses
When it happens
Trigger: A Followers GraphQL response contains a user- entry whose itemContent.user_results.result has __typename 'User' but both core.screen_name and legacy.screen_name are absent/empty — typically when Twitter ships a new GraphQL payload shape or returns a partially redacted/suspended user object.
Common situations: Twitter/X changes its GraphQL response schema (A/B tests, API versions); a follower account is suspended or restricted so fields are stripped; a proxy or mock returns truncated user objects during testing.
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
- Twitter UserMedia returned malformed user result
- Twitter UserMedia returned malformed timeline instructions
- Twitter UserMedia returned a tweet without rest_id
- 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/1425b8cda28e114a.
Report an issue: GitHub.