{"record":{"id":"73daa6318f22ed95","repo":"DIYgod/RSSHub","slug":"failed-to-extract-required-data-from-json","errorCode":null,"errorMessage":"Failed to extract required data from JSON","messagePattern":"Failed to extract required data from JSON","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/ixigua/user-video.tsx","lineNumber":55,"sourceCode":"    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) => ({\n            title: i.title,\n            description: renderToString(<IxiguaVideoDescription i={i} disableEmbed={disableEmbed} />),\n            link: `${host}/${i.groupId}`,\n            pubDate: parseDate(i.publishTime * 1000),\n            author: userInfo.name,\n        })),\n    };\n}\n\nconst IxiguaVideoDescription = ({ i, disableEmbed }: { i: any; disableEmbed?: string }) => (\n    <>","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/ixigua/user-video.tsx#L37-L73","documentation":"Thrown after the SSR_HYDRATED_DATA script content is parsed into JSON but the expected AuthorVideoList.videoList or AuthorDetailInfo fields are absent. The route destructures these two paths from the parsed object; if either is falsy the feed cannot be built. This indicates the JSON shape changed or the regex captured an incomplete/truncated object.","triggerScenarios":"The regex /var\\s+data\\s*=\\s*(\\{.*?\\});/s uses a non-greedy match that stops at the first '} ;' sequence, which can truncate a deeply nested object and yield partial JSON missing the AuthorVideoList/AuthorDetailInfo keys. Also fires when the user genuinely has no videos (videoList is empty/undefined in the SSR payload) or when ixigua renames these keys in a frontend version bump.","commonSituations":"ixigua restructures its SSR data and renames AuthorVideoList to a different key. A user with a brand-new or deactivated account has an empty video list. The regex's non-greedy quantifier captures only the first nested object, losing the rest of the payload.","solutions":["Inspect the full jsData string to confirm whether AuthorVideoList and AuthorDetailInfo keys exist — if present but missed, the regex is truncating; switch to a balanced-brace extraction or parse the whole script body.","If the keys were renamed, update the destructuring to match the new field names.","Differentiate between 'no videos' and 'missing data' — return an empty feed (allowEmpty) for users with no videos instead of throwing.","Log the top-level keys of jsonData when the guard fires to accelerate diagnosis."],"exampleFix":"// before\nconst jsonData = JSON.parse(jsData.match(/var\\s+data\\s*=\\s*(\\{.*?\\});/s)?.[1].replaceAll('undefined', 'null') || '{}');\n\n// after — extract the full balanced object instead of a non-greedy truncation\nconst start = jsData.indexOf('{');\nlet depth = 0, end = -1;\nfor (let i = start; i < jsData.length; i++) {\n    if (jsData[i] === '{') depth++;\n    else if (jsData[i] === '}') { depth--; if (depth === 0) { end = i; break; } }\n}\nconst jsonData = JSON.parse(jsData.slice(start, end + 1).replaceAll('undefined', 'null'));","handlingStrategy":"type-guard","validationCode":"// After parsing, verify the expected keys exist before destructuring\nconst jsonData = JSON.parse(rawJson);\nif (!('AuthorVideoList' in jsonData) || !('AuthorDetailInfo' in jsonData)) {\n    throw new Error(`Unexpected ixigua JSON shape. Top-level keys: ${Object.keys(jsonData).join(', ')}`);\n}","typeGuard":"interface IxiguaData {\n    AuthorVideoList: { videoList: unknown[] };\n    AuthorDetailInfo: { name: string; introduce?: string };\n}\nfunction isIxiguaData(d: unknown): d is IxiguaData {\n    return typeof d === 'object' && d !== null &&\n        'AuthorVideoList' in d && 'AuthorDetailInfo' in d &&\n        Array.isArray((d as any).AuthorVideoList?.videoList);\n}","tryCatchPattern":"try {\n    const jsonData = JSON.parse(jsData);\n    if (!isIxiguaData(jsonData)) {\n        throw new Error('Failed to extract required data from JSON');\n    }\n    // ...use jsonData with full type safety\n} catch (e) {\n    throw new Error(`ixigua JSON parse failed: ${(e as Error).message}`, { cause: e });\n}","preventionTips":["Use a balanced-brace extractor instead of a non-greedy regex to avoid truncated JSON.","Define a TypeScript interface for the expected payload and type-guard before destructuring.","Log top-level keys on failure to detect upstream schema changes early.","Distinguish 'user has zero videos' (empty array) from 'data missing' (undefined) — the former should not error."],"tags":["json-parsing","regex","data-shape-change","ixigua"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}