{"record":{"id":"e798a2b6d9d41ad5","repo":"DIYgod/RSSHub","slug":"bilibili-browser-response-does-not-contain-video-l","errorCode":null,"errorMessage":"Bilibili browser response does not contain video list data","messagePattern":"Bilibili browser response does not contain video list data","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/bilibili/video.ts","lineNumber":204,"sourceCode":"\n            await page.route('**/*', (route) => {\n                const request = route.request();\n                allowedBrowserRequestTypes.has(request.resourceType()) ? route.continue() : route.abort();\n            });\n        },\n        gotoConfig: { waitUntil: 'domcontentloaded' },\n    });\n\n    try {\n        const response = await waitForVideoListResponseFromVideoPage(page, videoUrl);\n        const data = (await response.json()) as VideoListResponse;\n        if (data.code) {\n            logger.error(JSON.stringify(data.data));\n            throw new Error(`Got error code ${data.code} while fetching in browser mode: ${data.message}`);\n        }\n\n        if (!data.data) {\n            throw new Error('Bilibili browser response does not contain video list data');\n        }\n\n        return data.data;\n    } finally {\n        await destroy();\n    }\n}\n\nasync function getVideoList(uid: string): Promise<VideoListData> {\n    try {\n        return await fetchVideoListFromApi(uid);\n    } catch (error) {\n        logger.warn(`[bilibili/video] API request failed, falling back to browser mode: ${error}`);\n        return fetchVideoListFromBrowser(uid);\n    }\n}\n\nasync function handler(ctx: Context) {","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/bilibili/video.ts#L186-L222","documentation":"Thrown by fetchVideoListFromBrowser after Bilibili's video-list XHR was captured but its JSON body contained a falsy `data` field (response.code was 0 yet no payload). This is the browser-mode fallback path; getVideoList only reaches it after the plain API request already failed. It signals that Bilibili served a well-formed but empty/envelope-only response to the headless browser, so there is genuinely nothing to render.","triggerScenarios":"The headless Playwright page loaded space.bilibili.com/{uid}/video and waitForVideoListResponseFromVideoPage resolved an XHR whose JSON has `code: 0` but no `data` key (or `data: null`). Common when the uid has zero public videos, the space requires login the cookie does not satisfy, or Bilibili anti-bot returns a stub response to the detected headless client.","commonSituations":"Running RSSHub without a valid BILIBILI_COOKIE_*; Bilibili tightening headless detection after a version bump; requesting a uid that deleted all videos or set them private; Playwright/Chromium not installed so resource blocking aborts the real XHR.","solutions":["Set a valid BILIBILI_COOKIE_{uid} (at least SESSDATA) in config so the browser session is authenticated.","Verify Playwright browsers are installed (`npx playwright install chromium`) and that resource-type allow-list includes xhr/document.","Check the uid actually has public videos on the source site; if not, the empty response is legitimate.","Inspect server logs for the prior API-mode failure that forced the browser fallback and address that root cause first."],"exampleFix":"// No code fix recommended — this is data-shape validation.\n// Mitigate by ensuring an authenticated cookie is applied:\n// config: BILIBILI_COOKIE_<uid> with a fresh SESSDATA","handlingStrategy":"validation","validationCode":"// Before relying on the browser response, validate the envelope shape.\nconst data = (await response.json()) as VideoListResponse;\nif (data.code) { throw new Error(`code ${data.code}`); }\nif (!data.data || !Array.isArray(data.data.list?.vlist)) {\n    throw new Error('Bilibili browser response does not contain video list data');\n}","typeGuard":"const isVideoListData = (d: unknown): d is VideoListData =>\n    typeof d === 'object' && d !== null &&\n    Array.isArray((d as any).list?.vlist);","tryCatchPattern":"// getVideoList already wraps API in try/catch and falls back to browser.\n// Wrap the browser call too and degrade gracefully rather than throwing raw:\ntry { return await fetchVideoListFromBrowser(uid); }\ncatch (e) { logger.error(`browser fallback failed: ${e}`); return { list: { vlist: [] } }; }","preventionTips":["Keep a valid BILIBILI_COOKIE_{uid} configured so the API path succeeds without needing the browser fallback.","Install Playwright Chromium and pin a tested browser version.","Log the prior API-mode error so the root cause of the fallback is visible."],"tags":["bilibili","playwright","anti-crawl","response-shape","fallback"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}