{"record":{"id":"c21ce2f410d7a0b7","repo":"jackwener/OpenCLI","slug":"bilibili-view-api-returned-a-malformed-payload-dur","errorCode":null,"errorMessage":"Bilibili view API returned a malformed payload during paid-content pre-check","messagePattern":"Bilibili view API returned a malformed payload during paid-content pre-check","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/bilibili/download.js","lineNumber":39,"sourceCode":"\nfunction isObject(value) {\n    return value && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * 下载前付费预检：付费/会员视频 yt-dlp 只能拿到试看流或直接失败，\n * 与其跑一半吐一坨 yt-dlp stderr，不如提前抛结构化 PAID_CONTENT（exit 77）。\n *\n * 大会员专享（vip）会再查一次 nav API：当前账号大会员有效就放行（cookie 喂给\n * yt-dlp 能下完整流）。ugc_pay / upower 的购买/充电状态没有廉价查询端点，保守\n * 拦截，已购用户用 --force 跳过。预检自身的 API 失败不阻塞下载（保持旧行为）。\n */\nasync function assertNotPaidContent(page, bvid) {\n    let d;\n    try {\n        const payload = await apiGet(page, '/x/web-interface/view', { params: { bvid } });\n        if (!isObject(payload) || !Object.hasOwn(payload, 'code')) {\n            throw new CommandExecutionError('Bilibili view API returned a malformed payload during paid-content pre-check');\n        }\n        if (payload.code !== 0)\n            return;\n        if (!isObject(payload.data) || !isObject(payload.data.rights)) {\n            throw new CommandExecutionError('Bilibili view API returned malformed paid-content metadata');\n        }\n        d = payload.data;\n    }\n    catch (error) {\n        if (error instanceof CommandExecutionError) {\n            throw error;\n        }\n        return;\n    }\n    const rights = d.rights;\n    const paymentType = rights.pay\n        ? 'vip'\n        : (rights.ugc_pay || rights.arc_pay)","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/bilibili/download.js#L21-L57","documentation":"During the pre-download paid-content check, the /x/web-interface/view response either wasn't an object or lacked a `code` field, so its payment rights cannot be evaluated. The library throws this structured error instead of assuming the video is free (unlike network errors, which are swallowed and treated as pass-through).","triggerScenarios":"apiGet to /x/web-interface/view succeeds at transport level but returns a non-object body, HTML instead of JSON, or JSON without `code` — e.g. Bilibili risk-control interstitial, gateway HTML error page, or API format change.","commonSituations":"Bilibili anti-bot risk control serving HTML challenges; region-locked or CDN edge returning error pages; expired/invalid cookies producing an unexpected body shape; corporate proxy injecting content.","solutions":["Re-run the command; risk-control interstitials are often transient.","Refresh the stored Bilibili cookies / re-login so requests aren't challenged.","Check network path (proxy/VPN) for HTML injection or blocked domains.","Clear cookies entirely to bypass pre-check failures (pre-check failures other than this structured error are non-fatal by design)."],"exampleFix":"// caller-side guard mirroring the check\nconst payload = await apiGet(page, '/x/web-interface/view', { params: { bvid } });\nif (!payload || typeof payload !== 'object' || Array.isArray(payload) || !Object.hasOwn(payload, 'code')) {\n    // treat as malformed: skip pre-check or retry\n}","handlingStrategy":"try-catch","validationCode":"const res = await fetch('https://api.bilibili.com/x/web-interface/view?bvid=' + bvid, { headers: { cookie } });\nconst body = await res.text();\nlet payload; try { payload = JSON.parse(body); } catch { /* HTML/risk-control page */ }\nif (!payload || typeof payload !== 'object' || Array.isArray(payload) || !('code' in payload)) { /* retry or refresh cookies */ }","typeGuard":"function isViewEnvelope(v) {\n    return Boolean(v) && typeof v === 'object' && !Array.isArray(v) && Object.hasOwn(v, 'code');\n}","tryCatchPattern":"try {\n    await download(bvid);\n} catch (e) {\n    if (/malformed payload during paid-content pre-check/.test(e.message)) {\n        // refresh cookies / retry; risk-control interstitials are often transient\n    } else throw e;\n}","preventionTips":["Keep Bilibili cookies fresh to avoid risk-control challenges","Avoid heavy request rates that trigger anti-bot interstitials","Don't route through proxies that inject/modify HTML","Retry once on malformed envelopes before escalating"],"tags":["bilibili","api","malformed-response","risk-control"],"backgroundTag":"malformed-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}