DIYgod/RSSHub · error · Error

Failed to find SSR_HYDRATED_DATA

Error message

Failed to find SSR_HYDRATED_DATA

What it means

Thrown by the ixigua user-video route when the fetched user homepage HTML does not contain a <script id="SSR_HYDRATED_DATA"> element. The route scrapes server-side-rendered JSON embedded in that tag to obtain the video list and user info. When ixigua serves a different page (anti-bot wall, login redirect, redesigned DOM, or an invalid/expired UID), the selector returns undefined and this guard fires.

Source

Thrown at lib/routes/ixigua/user-video.tsx:44

            target: '/user/video/:uid',
        },
    ],
    name: '用户视频投稿',
    maintainers: ['FlashWingShadow', 'Fatpandac', 'pseudoyu'],
    handler,
};

async function handler(ctx) {
    const uid = ctx.req.param('uid');
    const disableEmbed = ctx.req.param('disableEmbed');
    const url = `${host}/home/${uid}/?wid_try=1`;

    const { data } = await got(url);
    const $ = load(data);
    const jsData = $('#SSR_HYDRATED_DATA').html();

    if (!jsData) {
        throw new Error('Failed to find SSR_HYDRATED_DATA');
    }

    const jsonData = JSON.parse(jsData.match(/var\s+data\s*=\s*(\{.*?\});/s)?.[1].replaceAll('undefined', 'null') || '{}');

    const {
        AuthorVideoList: { videoList: videoInfos },
        AuthorDetailInfo: userInfo,
    } = jsonData;

    if (!videoInfos || !userInfo) {
        throw new Error('Failed to extract required data from JSON');
    }

    return {
        title: `${userInfo.name} 的西瓜视频`,
        link: url,
        description: userInfo.introduce,
        item: videoInfos.map((i) => ({

View on GitHub (pinned to bed535e087)

Solutions

  1. Verify the UID is correct by opening https://www.ixigua.com/home/{uid}/ in a browser and confirming the profile loads with videos.
  2. Check whether ixigua is returning an anti-bot/verification page — inspect the raw response body; if so the route needs updated headers, cookies, or Puppeteer handling.
  3. If the DOM changed, update the selector $('#SSR_HYDRATED_DATA') and the regex /var\s+data\s*=\s*(\{.*?\});/s to match the new embedding format.
  4. Add a more descriptive error that includes the response URL and a snippet of the body to aid future debugging.

Example fix

// before
const jsData = $('#SSR_HYDRATED_DATA').html();
if (!jsData) {
    throw new Error('Failed to find SSR_HYDRATED_DATA');
}

// after — surface what was actually returned so the failure is diagnosable
const jsData = $('#SSR_HYDRATED_DATA').html();
if (!jsData) {
    const snippet = $.html().slice(0, 200);
    throw new Error(`Failed to find SSR_HYDRATED_DATA for uid ${uid}. Response started with: ${snippet}`);
}
Defensive patterns

Strategy: validation

Validate before calling

// Before fetching, sanity-check the uid is a non-empty numeric string
const uid = ctx.req.param('uid');
if (!/^\d+$/.test(uid)) {
    throw new InvalidParameterError(`uid must be numeric, got '${uid}'`);
}
// Then fetch and verify the response looks like a profile page before selecting
const { data } = await got(url);
if (!data.includes('SSR_HYDRATED_DATA')) {
    // likely an anti-bot page; surface a diagnostic instead of the generic error
    throw new Error(`ixigua did not return SSR data for uid ${uid}; possible anti-bot block`);
}

Type guard

function hasSsrHydratedData($: cheerio.CheerioAPI): $ is cheerio.CheerioAPI & { __ssr: true } {
    return $('#SSR_HYDRATED_DATA').html() != null;
}

Try / catch

try {
    const jsData = $('#SSR_HYDRATED_DATA').html();
    if (!jsData) throw new Error('Failed to find SSR_HYDRATED_DATA');
    // ...process
} catch (e) {
    // Log the response URL + body length, then rethrow with context
    throw new Error(`${(e as Error).message} (uid=${uid}, bodyLen=${data.length})`, { cause: e });
}

Prevention

When it happens

Trigger: Calling /ixigua/user/video/:uid where the UID is invalid, deleted, or the user has no public videos. Also triggered when ixigua returns an anti-crawler interstitial, a Cookie/verification page, or when the site redesigns and renames the SSR_HYDRATED_DATA script tag. A got() response that is HTML but not the expected profile page (e.g. a 200-status error page) also triggers it.

Common situations: ixigua tightens anti-crawler measures and starts returning a verification challenge instead of the profile HTML. The user copies the wrong UID (e.g. a short numeric id instead of the long internal one). The site ships a frontend rewrite and the embedded data script id changes. RSSHub's got() request is rate-limited and gets a throttle page.

Related errors


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