DIYgod/RSSHub · error · Error

Failed to fetch followings data

Error message

Failed to fetch followings data

What it means

Thrown at lib/routes/skeb/utils.tsx:136 after fetching `https://skeb.jp/api/users/{username}/followings` with a bearer-token Authorization header. The guard checks that `followings_data` is truthy and an object before calling `.map()` on it. If Skeb's API returns null, an empty string, a 401/403 error body, or a redirect HTML page (ofetch follows redirects and parses whatever comes back), the check fails and the error fires.

Source

Thrown at lib/routes/skeb/utils.tsx:136

export async function getFollowingsItems(username: string, path: 'friend_works' | 'following_works' | 'following_creators'): Promise<DataItem[]> {
    const url = `${baseUrl}/api/users/${username.replace('@', '')}/followings`;

    const followings_data = await cache.tryGet(
        `skeb:followings_data:${username}`,
        async () => {
            const data = await ofetch(url, {
                headers: {
                    Authorization: `Bearer ${config.skeb.bearerToken}`,
                },
            });
            return data;
        },
        config.cache.routeExpire,
        false
    );

    if (!followings_data || typeof followings_data !== 'object') {
        throw new Error('Failed to fetch followings data');
    }

    if (path === 'following_creators') {
        return followings_data[path].map((item) => processCreator(item)).filter(Boolean) as DataItem[];
    }
    return followings_data[path].map((item) => processWork(item)).filter(Boolean) as DataItem[];
}

const SkebWorkDescription = ({ imageUrl, body, audioUrl }: { imageUrl?: string; body: string; audioUrl?: string | null }) => (
    <>
        {imageUrl ? (
            <>
                <img src={imageUrl} />
                <br />
            </>
        ) : null}
        {audioUrl ? (
            <>

View on GitHub (pinned to bed535e087)

Solutions

  1. Set a fresh SKEB_BEARER_TOKEN in config by logging into skeb.jp and copying the Authorization Bearer value from a real API request in DevTools.
  2. Verify the username exists by opening `https://skeb.jp/@{username}` in a browser.
  3. Temporarily log the raw `followings_data` value to see what Skeb actually returned (it may be an auth-error JSON).
  4. Clear the cache key `skeb:followings_data:{username}` if stale bad data was cached.
Defensive patterns

Strategy: try-catch

Validate before calling

if (!config.skeb.bearerToken) {
    throw new ConfigNotFoundError('SKEB_BEARER_TOKEN is not set');
}
const cleanUsername = username.replace('@', '');
if (!cleanUsername) {
    throw new InvalidParameterError('username is required');
}

Type guard

const isFollowingsResponse = (d: unknown): d is Record<string, unknown[]> =>
    !!d && typeof d === 'object' && !Array.isArray(d);

Try / catch

try {
    const items = await getFollowingsItems(username, path);
} catch (e) {
    if (e instanceof Error && e.message === 'Failed to fetch followings data') {
        // likely auth or upstream issue; check bearerToken freshness
        throw new Error('Skeb followings unavailable — verify SKEB_BEARER_TOKEN and username');
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling getFollowingsItems with a username that does not exist; the SKEB_BEARER_TOKEN is expired or revoked so Skeb returns a JSON error object instead of the followings payload; Cloudflare or Skeb rate-limiting returns an interstitial HTML page; the username still has a leading '@' that wasn't stripped correctly (line 119 should strip it but edge cases exist).

Common situations: SKEB_BEARER_TOKEN environment variable not set or left empty; token harvested from a logged-out session; Skeb changed their API response shape; the user passed a screen_name that contains special characters.

Related errors


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