{"record":{"id":"95c85ab5d5b5c767","repo":"DIYgod/RSSHub","slug":"failed-to-find-ssr-hydrated-data","errorCode":null,"errorMessage":"Failed to find SSR_HYDRATED_DATA","messagePattern":"Failed to find SSR_HYDRATED_DATA","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/ixigua/user-video.tsx","lineNumber":44,"sourceCode":"            target: '/user/video/:uid',\n        },\n    ],\n    name: '用户视频投稿',\n    maintainers: ['FlashWingShadow', 'Fatpandac', 'pseudoyu'],\n    handler,\n};\n\nasync function handler(ctx) {\n    const uid = ctx.req.param('uid');\n    const disableEmbed = ctx.req.param('disableEmbed');\n    const url = `${host}/home/${uid}/?wid_try=1`;\n\n    const { data } = await got(url);\n    const $ = load(data);\n    const jsData = $('#SSR_HYDRATED_DATA').html();\n\n    if (!jsData) {\n        throw new Error('Failed to find SSR_HYDRATED_DATA');\n    }\n\n    const jsonData = JSON.parse(jsData.match(/var\\s+data\\s*=\\s*(\\{.*?\\});/s)?.[1].replaceAll('undefined', 'null') || '{}');\n\n    const {\n        AuthorVideoList: { videoList: videoInfos },\n        AuthorDetailInfo: userInfo,\n    } = jsonData;\n\n    if (!videoInfos || !userInfo) {\n        throw new Error('Failed to extract required data from JSON');\n    }\n\n    return {\n        title: `${userInfo.name} 的西瓜视频`,\n        link: url,\n        description: userInfo.introduce,\n        item: videoInfos.map((i) => ({","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/ixigua/user-video.tsx#L26-L62","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the UID is correct by opening https://www.ixigua.com/home/{uid}/ in a browser and confirming the profile loads with videos.","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.","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.","Add a more descriptive error that includes the response URL and a snippet of the body to aid future debugging."],"exampleFix":"// before\nconst jsData = $('#SSR_HYDRATED_DATA').html();\nif (!jsData) {\n    throw new Error('Failed to find SSR_HYDRATED_DATA');\n}\n\n// after — surface what was actually returned so the failure is diagnosable\nconst jsData = $('#SSR_HYDRATED_DATA').html();\nif (!jsData) {\n    const snippet = $.html().slice(0, 200);\n    throw new Error(`Failed to find SSR_HYDRATED_DATA for uid ${uid}. Response started with: ${snippet}`);\n}","handlingStrategy":"validation","validationCode":"// Before fetching, sanity-check the uid is a non-empty numeric string\nconst uid = ctx.req.param('uid');\nif (!/^\\d+$/.test(uid)) {\n    throw new InvalidParameterError(`uid must be numeric, got '${uid}'`);\n}\n// Then fetch and verify the response looks like a profile page before selecting\nconst { data } = await got(url);\nif (!data.includes('SSR_HYDRATED_DATA')) {\n    // likely an anti-bot page; surface a diagnostic instead of the generic error\n    throw new Error(`ixigua did not return SSR data for uid ${uid}; possible anti-bot block`);\n}","typeGuard":"function hasSsrHydratedData($: cheerio.CheerioAPI): $ is cheerio.CheerioAPI & { __ssr: true } {\n    return $('#SSR_HYDRATED_DATA').html() != null;\n}","tryCatchPattern":"try {\n    const jsData = $('#SSR_HYDRATED_DATA').html();\n    if (!jsData) throw new Error('Failed to find SSR_HYDRATED_DATA');\n    // ...process\n} catch (e) {\n    // Log the response URL + body length, then rethrow with context\n    throw new Error(`${(e as Error).message} (uid=${uid}, bodyLen=${data.length})`, { cause: e });\n}","preventionTips":["Validate the uid format before the network call.","Cache the SSR data extraction with cache.tryGet keyed on uid to reduce repeated anti-bot exposure.","Monitor for a spike in this error — it signals an upstream site change or block.","If ixigua blocks RSSHub's IP, consider Puppeteer-based fetching with the requirePuppeteer feature flag."],"tags":["scraping","anti-crawler","dom-selector","ixigua"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}