{"record":{"id":"df73ab0da8a357d5","repo":"jackwener/OpenCLI","slug":"p-payload-message-unknown-pa","errorCode":null,"errorMessage":"获取视频分P信息失败: ${payload?.message ?? 'unknown'} (${payload?.code ?? 'malformed'})","messagePattern":"获取视频分P信息失败: (.+?) \\((.+?)\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/bilibili/download.js","lineNumber":91,"sourceCode":"    }\n    throw new CliError(\n        'PAID_CONTENT',\n        `该视频为付费内容（${PAYMENT_LABELS[paymentType]}），当前账号无观看权益，无法获取完整视频流`,\n        '若已购买/已充电/已开通会员，加 --force 跳过本检查直接下载',\n        EXIT_CODES.NOPERM,\n    );\n}\n\nasync function loadSelectedPart(page, bvid, pageNum) {\n    let payload;\n    try {\n        payload = await apiGet(page, '/x/web-interface/view', { params: { bvid } });\n    }\n    catch (error) {\n        throw new CommandExecutionError(`获取视频分P信息失败: ${error?.message || error}`);\n    }\n    if (!isObject(payload) || payload.code !== 0) {\n        throw new CommandExecutionError(`获取视频分P信息失败: ${payload?.message ?? 'unknown'} (${payload?.code ?? 'malformed'})`);\n    }\n    return selectVideoPart(payload.data, pageNum);\n}\n\ncli({\n    site: 'bilibili',\n    name: 'download',\n    access: 'read',\n    description: '下载B站视频（需要 yt-dlp）',\n    domain: 'www.bilibili.com',\n    strategy: Strategy.COOKIE,\n    args: [\n        { name: 'bvid', required: true, positional: true, help: 'Video BV ID (e.g., BV1xxx)' },\n        { name: 'output', default: './bilibili-downloads', help: 'Output directory' },\n        { name: 'quality', default: 'best', help: 'Video quality (best, 1080p, 720p, 480p)' },\n        { name: 'force', type: 'boolean', default: false, help: '跳过付费内容预检直接下载（已购买/已充电/已开通会员时用）' },\n        { name: 'page', required: false, help: '分P 选集序号（从 1 开始）。多 P 视频下载该集；缺省下载默认 P1' },\n    ],","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/bilibili/download.js#L73-L109","documentation":"loadSelectedPart calls the Bilibili web-interface/view API to fetch video metadata (including pages/分P list). After the HTTP call succeeds, it validates that the response is an object with code===0; anything else (error payload, non-zero code, non-object body) is rethrown as a CommandExecutionError embedding the API message and code. This distinguishes API-level rejection from transport errors, which are thrown by the preceding catch.","triggerScenarios":"Calling bilibili download with a bvid whose view API responds with code!==0 (e.g. -404 video not found, -400 invalid bvid, -352 risk-control) or with a body that is not a JSON object.","commonSituations":"Typo in the BV id, deleted/private/region-locked video, Bilibili risk control returning -352/-412, or a proxy/captive portal returning an HTML error page that fails object validation (code 'malformed').","solutions":["Verify the bvid is correct and the video is public (opens at bilibili.com/video/<bvid>).","Retry later or from a cleaner IP if code is -352/-412 (risk control); use cookies of a logged-in session.","If code is 'malformed', inspect the raw response — you are likely behind a proxy/firewall returning non-JSON.","Handle the payload.code in calling code to give the end user a specific message (e.g. -404 → 'video not found')."],"exampleFix":"// before\nconst part = await loadSelectedPart(page, bvid, pageNum);\n// after\nlet part;\ntry {\n  part = await loadSelectedPart(page, bvid, pageNum);\n} catch (e) {\n  if (String(e.message).includes('-404')) throw new Error(`视频不存在: ${bvid}`);\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"if (!/^BV[0-9A-Za-z]{10}$/.test(bvid)) throw new Error(`invalid bvid: ${bvid}`);","typeGuard":"function isOkPayload(p) {\n  return typeof p === 'object' && p !== null && p.code === 0 && 'data' in p;\n}","tryCatchPattern":"try {\n  const part = loadSelectedPart(page, bvid, pageNum);\n} catch (e) {\n  const m = String(e.message);\n  if (m.includes('-404')) return null; // video gone\n  if (m.includes('-352') || m.includes('-412')) return retryWithBackoff();\n  throw e;\n}","preventionTips":["Validate bvid format (BV + 10 alphanumerics) before calling","Check the video opens in a browser before automating","Use a logged-in session to reduce risk-control responses","Rate-limit calls to avoid -412/-352"],"tags":["bilibili","api-error","network"],"backgroundTag":"api-error-response-code","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}