{"record":{"id":"da364e1393657970","repo":"DIYgod/RSSHub","slug":"got-error-code-data-code-while-fetching-in-brow","errorCode":null,"errorMessage":"Got error code ${data.code} while fetching in browser mode: ${data.message}","messagePattern":"Got error code (.+?) while fetching in browser mode: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/bilibili/video.ts","lineNumber":200,"sourceCode":"        onBeforeLoad: async (page) => {\n            if (cookie) {\n                await applyCookie(page, cookie);\n            }\n\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);","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/bilibili/video.ts#L182-L218","documentation":"Thrown by fetchVideoListFromBrowser (the Playwright fallback) after it captured a 200 JSON response from wbi/arc/search but the parsed body has a non-zero `code`. It mirrors error 118 but for the browser path: the request succeeded at the network layer yet Bilibili rejected it at the application layer. Because this only fires after the API path already failed, hitting it means both code paths are being rejected.","triggerScenarios":"getVideoList(uid): fetchVideoListFromApi threw (error 118), so the browser fallback runs; the browser-side wbi/arc/search XHR returns 200 JSON with code !== 0 — typically -352/-479 (risk control, now also flagging the browser session) or -101 (the applied cookie is invalid/expired).","commonSituations":"BILIBILI_COOKIE_* is expired or belongs to a restricted account, so both the direct API and the browser session are rejected; RSSHub IP under heavy anti-crawler ban; buvid/wbi state missing in the browser context.","solutions":["Refresh BILIBILI_COOKIE_{uid} from a new bilibili.com login — it is shared by both paths via cache.getConfiguredCookie/applyCookie.","If no cookie is set, configure one; the browser path currently relies on an anonymous session which bilibili commonly rejects with -352.","Rotate the egress IP or slow down polling — a -352 on the browser path means the IP itself is flagged.","Inspect the logged data.data (logger.error(JSON.stringify(data.data))) to decode the exact code; -101 = session, -352/-479 = risk control.","If both paths persistently fail, the account/IP is hard-blocked; pause the feed and use a different account or proxy."],"exampleFix":"// before\nconst data = (await response.json()) as VideoListResponse;\nif (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// after (distinguish auth from risk control)\nconst data = (await response.json()) as VideoListResponse;\nif (data.code) {\n    logger.error(`[bilibili/video] browser api code ${data.code}: ${JSON.stringify(data.data)}`);\n    if (data.code === -101) {\n        throw new ConfigNotFoundError(`Bilibili cookie invalid/expired in browser mode (uid ${uid})`);\n    }\n    throw new Error(`Got error code ${data.code} while fetching in browser mode: ${data.message ?? 'no message'}`);\n}","handlingStrategy":"retry","validationCode":"// Confirm the cookie that applyCookie will inject is still valid before entering browser mode.\nimport ofetch from '@/utils/ofetch';\nimport cache from './cache';\nasync function browserCookieValid() {\n  const cookie = cache.getConfiguredCookie();\n  if (!cookie) return false;\n  const nav = await ofetch<{ code: number }>('https://api.bilibili.com/x/web-interface/nav', { headers: { Cookie: cookie } });\n  return nav.code === 0;\n}","typeGuard":"interface VideoListResponse { code?: number; data?: VideoListData; message?: string }\nfunction isVideoListOk(r: VideoListResponse): r is { code: 0; data: VideoListData } {\n  return r.code === 0 && !!r.data;\n}","tryCatchPattern":"// At the getVideoList() call site: try API, then browser once, then give up with a clear message.\ntry {\n  return await fetchVideoListFromApi(uid);\n} catch (apiErr) {\n  try {\n    return await fetchVideoListFromBrowser(uid);\n  } catch (browserErr) {\n    const m = String(browserErr);\n    if (m.includes('-101')) throw new Error(`Bilibili cookie expired for uid ${uid} (refresh BILIBILI_COOKIE_*)`);\n    if (m.includes('-352') || m.includes('-479')) throw new Error(`Bilibili risk control active for uid ${uid} (rotate IP/account, slow down)`);\n    throw browserErr;\n  }\n}","preventionTips":["Refresh BILIBILI_COOKIE_* the moment -101 appears on either path — the same cookie drives both.","When both API and browser paths return -352/-479, the egress IP is flagged; rotate IP or pause the feed rather than retrying in a tight loop.","Confirm buvid/wbi state is present in the browser context (applyCookie covers session cookies; wbi keys are shared via cache).","Cache the video list for several minutes so a hard block does not cascade into repeated browser launches."],"tags":["bilibili","api-error","playwright","browser","authentication","anti-crawler","rsshub"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}