DIYgod/RSSHub · warning · InvalidParameterError

User not found

Error message

User not found

What it means

Thrown by RSSHub's Twitter web-api when the GraphQL UserByRestId / UserByScreenName response contains no user.result.rest_id. RSSHub treats a missing rest_id as 'the screen name or numeric id does not resolve to an account', caches an empty placeholder so the lookup is not retried, and surfaces InvalidParameterError('User not found') so the HTTP layer returns a 400-class error to the feed reader.

Source

Thrown at lib/routes/twitter/api/web-api/api.ts:50

                method: 'GET',
                params,
                headers: {
                    'accept-encoding': 'gzip',
                },
            });
        }

        return twitterGot(`${baseUrl}${id.startsWith('+') ? gqlMap.UserByRestId : gqlMap.UserByScreenName}`, params, {
            allowNoAuth: !id.startsWith('+'),
        });
    });

const cacheTryGet = async (_id, params, operationName, func) => {
    const userData: any = await getUserData(_id);
    const id = userData.data?.user?.result?.rest_id;
    if (id === undefined) {
        cache.set(`twitter-userdata-${_id}`, '', config.cache.contentExpire);
        throw new InvalidParameterError('User not found');
    }
    return cache.tryGet(getTwitterUserCacheKey(id, operationName, params), () => func(id, params), config.cache.routeExpire, false);
};

const getUserTweets = (id: string, params?: Record<string, any>) =>
    cacheTryGet(id, params, 'getUserTweets', async (id, params = {}) =>
        gatherLegacyFromData(
            await paginationTweets('UserTweets', id, {
                ...params,
                count: 20,
                includePromotedContent: true,
                withQuickPromoteEligibilityTweetFields: true,
                withVoice: true,
                withV2Timeline: true,
            })
        )
    );

View on GitHub (pinned to bed535e087)

Solutions

  1. Verify the screen name resolves on x.com in a browser (open https://x.com/<id>).
  2. Prefix the id with '+' to force the numeric UserByRestId path, or remove the '+' to use screen-name lookup, matching the actual identifier type.
  3. Remove the cached empty placeholder by evicting the cache key 'twitter-userdata-<id>' (contentExpire window) so the lookup is re-attempted after the account is restored.
  4. If the account was renamed, update the feed URL to the new handle.

Example fix

// before
// /twitter/user/someoldhandle  (renamed)
// after
// /twitter/user/newhandle

// or force numeric-id lookup
// /twitter/user/+1234567890
Defensive patterns

Strategy: validation

Validate before calling

// Before subscribing, sanity-check the handle resolves.
import ofetch from 'ofetch';
async function twitterUserExists(handle: string): Promise<boolean> {
  try {
    const r = await ofetch(`https://x.com/i/api/graphql/xmU6X_CKVnQ5lSrCbAmJsg/UserByScreenName?variables=${encodeURIComponent(JSON.stringify({ screen_name: handle.replace(/^@/, ''), withSafetyModeUserFields: true }))}`, { headers: { authorization: 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D' } });
    return Boolean(r?.data?.user?.result?.rest_id);
  } catch { return false; }
}

Type guard

function isTwitterUserResult(userData: any): userData is { data: { user: { result: { rest_id: string } } } } {
  return typeof userData?.data?.user?.result?.rest_id === 'string';
}

Try / catch

// RSSHub operators usually cannot edit the route; end-users should wrap feed fetches.
try {
  await fetchRss('/twitter/user/<handle>');
} catch (e) {
  if (/User not found/.test(String(e))) { /* remove/fix the feed */ }
  else throw e;
}

Prevention

When it happens

Trigger: Calling a Twitter user route (/twitter/user/:id) with a misspelled or deleted screen name, a numeric id of a suspended account, or an id whose response shape changed (Twitter occasionally drops rest_id for protected/age-gated accounts). The check fires only after userData.data.user.result.rest_id === undefined, i.e. the API answered but did not include a rest id.

Common situations: Feed URL hard-codes an old handle that the user renamed or deleted; copy-paste of a handle with a leading '@' that the route does not strip; Twitter returns a UserUnavailable payload that still has data.user but no result.rest_id; transient API outage returning a partial JSON body.

Related errors


AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12). Data as JSON: /api/errors/c3cd2710eba46810. Report an issue: GitHub.